8/30〜|ライターのダイチが運営している個人メディア「ダイブログ」とPENGIN BLOGを統合しました

【HTML&CSS】カード型レイアウト(モジュール)のコーディング方法

お知らせ

記事の元サイト「ダイブログ」は、8/30〜「PENGIN PLOG」と統合しました。
※元ドメイン(https://daib-log.com/〜)からはリダイレクト処理されます。

どうも、ダイチです。

今回はカード型レイアウトの作り方についてまとめました。

汎用的なWebデザインの一つですが、メディアクエリでカラムを変える時や、WordPresなどを想定して更新性を確保することを考えると意外と難しいです。

色々書き方を調べましたが、この記事で紹介するのはnth系の擬似クラスを使わずに簡単に構成できる方法となっています。

現時点での僕的ベストプラクティスなので、同じように悩んでる方は是非参考にしてみてください!

>> コードだけ先に見たい方はこちらから(スクロールします)

カード型レイアウトとは

カード型レイアウトとは、下のようにカードやタイル型のコンポーネントを並べるデザインのことです。

ブログサイトの記事一覧や、コーポレートサイトの実績紹介などで使われることも多く、同列の要素を一覧表示にしたい場合に取り入れられたりします。

レイアウト例

複数行になる場合、一般的にはコンテナ要素の枠の中で、左から詰めるように配置されます。

PC向けのレイアウトだったりするので、画面幅に合わせてカラム(表示数)を切り替えたりと、実装には工夫が必要です。

カード型レイアウトを構成する時の注意点

表示されるアイテム数が一定であればいいのですが、ブログサイトのように並ぶ数が変動するサイトは注意が必要です。

<ul class="bl_flexContainer">
    <li class="el_flexItem"></li>
    <li class="el_flexItem"></li>
    <li class="el_flexItem"></li>
</ul>
.bl_flexContainer {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	padding: 10px;
}
.el_flexItem {
	width: 30%;
	height: 300px;
	background-color: royalblue;
}

アイテムを並べるだけであれば、みんな大好きFlexBoxで実現できます。

flexbox

flex-wrap: wrapを指定しておけば、要素が増えた時にコンテナ内で自動で次の行に折り返してくれます。

また、カード型レイアウトでは左右のアイテムはコンテナ内の枠の端にぴったりと配置させることが多いので(もちろんpaddingも考慮したうえで)、justify-content: space-betweenを指定しています。

marginを指定せずにアイテムを等間隔で配置できるので楽チンです。

しかしアイテムが下のように増えると、

<ul class="bl_flexContainer">
    <li class="el_flexItem"></li>
    <li class="el_flexItem"></li>
    <li class="el_flexItem"></li>
    <li class="el_flexItem"></li>
    <li class="el_flexItem"></li>
</ul>
Image from Gyazo

このようになってしまいます。

表示要素の数が変動するブロックの場合、左右要素を端に寄せたいからといった理由で、安易にjustify-content: space-betweenを使うのは危険です。

nth系の擬似クラスを使った構成方法

space-betweenを使わずに要素間の余白を作るには、一つ一つにmarginを指定する必要があります。

当記事で紹介するのとは違うやり方ですが、比較のために、nth系の擬似クラスを使った方法を紹介しておき。

<ul class="bl_flexContainer">
    <li class="el_flexItem"></li>
    <li class="el_flexItem"></li>
    <li class="el_flexItem"></li>
    <li class="el_flexItem"></li>
    <li class="el_flexItem"></li>
</ul>
.bl_flexContainer {
	display: flex;
	flex-wrap: wrap;
	padding: 20px;
}
.el_flexItem {
	width: calc(100% / 3 - 40px / 3);
	height: 300px;
	margin-right: 20px;
	margin-bottom: 30px;
	background-color: royalblue;
}

3枚のカードを並べることを前提に指定しています。

margin-rightが入ってしまっている画像

各アイテムにmargin-right: 20pxとしていますが、このままだと当然右端にも余白ができてしまいます。

.bl_flexContainer {
	display: flex;
	flex-wrap: wrap;
	padding: 20px;
}
.el_flexItem {
	width: calc(100% / 3 - 40px / 3);
	height: 300px;
	margin-right: 20px;
	margin-bottom: 30px;
	background-color: royalblue;
}
.el_flexItem:nth-of-type(3n) {
	margin-right: 0;
}

これを解消するために、nth-of-type(3n)とすることで3の倍数の時だけmargin-rightを0として打ち消す記述を追加します。

width: calc(100% / 3 - 40px / 3);

アイテム幅のところも説明しておくと、100%/3で33.333pxとして、ここから余白幅を引いています。

カード幅計算方法

margin-rightでアイテム右に20px設けたので、カード3枚だったら余白合計は40px。これをアイテム数(3)で割った数値がマイナスする余白幅です。

widthを100%/3としているので、画面幅が変わっても3カラムのレイアウトが崩れることはありません。

@media screen and (max-width: 599px) {
	.el_flexItem {
		width: calc(100% / 2 - 20px / 2);
	}
	.el_flexItem:nth-of-type(2n) {
		margin-right: 0;
	}
}

メディアクエリでカラム数を変えたい場合は、カードのwidthで割る数(上の例だと2カラム)を変更します。

また、nth-of-typeの数値もカラム数にあわせて指定し、margin-rightを0とします。

Image from Gyazo

ただ、それだけだと上で指定した3nの指定が残ってしまっているので、打ち消しの指定も必要です。

@media screen and (max-width: 599px) {
	.el_flexItem {
		width: calc(100% / 2 - 20px / 2);
	}
	/* スタイルの打ち消し */
	.el_flexItem:nth-of-type(3n) {
		margin-right: 20px;
	}
	.el_flexItem:nth-of-type(2n) {
		margin-right: 0;
	}
}

注意点としては、詳細度の問題があるため、スタイルの打ち消し(上だと3n)より、新しい指定(上だと2n)は下に記述にしておく必要があります。

Image from Gyazo

もしくは親要素を被せたり別スタイルを重ねるなどで詳細度を上げる必要があります。

このように、カラム数とnthの数を合わせるやり方は考え方として分かりやすいですが、Sassでネストが深くなって詳細度が変わったり、メディアクエリが増えてカラムの調整が入る場合など、どうしても記述が面倒になります。

なので最近はこのnth系の指定方法をやめて、今回紹介する構成方法に切り替えることにしました。

HTML/CSSコード全体紹介

前置きが長くなりましたが、本題の解説を進めます。

今回紹介するやり方を簡単にお伝えすると、アイテム要素に指定した分のmarginを、コンテナ要素からネガティブマージンで相殺してすっぽり収める、みたいな感じです。

先に全体のコードを記載しておきます。このままコピペして使うこともできます。

<div class="bl_media_container">
    <div class="bl_media_itemWrapper">
      <div class="bl_media_item">
        <p class="img"><img src="img/..." alt=""></p>
        <h3>ダミータイトル</h3>
        <p>ダミーダミーダミーダミーダミーダミー</p>
      </div>
    </div>
    <div class="bl_media_itemWrapper">
      <div class="bl_media_item">
        <p class="img"><img src="img/..." alt=""></p>
        <h3>ダミータイトル</h3>
        <p>ダミーダミーダミーダミーダミーダミー</p>
      </div>
    </div>
    <div class="bl_media_itemWrapper">
      <div class="bl_media_item">
        <p class="img"><img src="img/..." alt=""></p>
        <h3>ダミータイトル</h3>
        <p>ダミーダミーダミーダミーダミーダミー</p>
      </div>
    </div>
    <div class="bl_media_itemWrapper">
      <div class="bl_media_item">
        <p class="img"><img src="img/..." alt=""></p>
        <h3>ダミータイトル</h3>
        <p>ダミーダミーダミーダミーダミーダミー</p>
      </div>
    </div>
    <div class="bl_media_itemWrapper">
      <div class="bl_media_item">
        <p class="img"><img src="img/..." alt=""></p>
        <h3>ダミータイトル</h3>
        <p>ダミーダミーダミーダミーダミーダミー</p>
      </div>
    </div>
  </div>
/* BASE CSS */
* {
	list-style: none;
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}
img {
	width: 100%;
	vertical-align: bottom;
}
/* ここからカードレイアウトのスタイリング */
/* PC 3カラム */
.bl_media_container {
	display: flex;
	flex-wrap: wrap;
	margin: calc(-30px / 2);
	padding: 30px;
}
.bl_media_itemWrapper {
	width: calc(100% / 3 - 30px);
	margin: calc(30px / 2);
}
.bl_media_item {
	outline: 1px solid #000;
	font-size: 1.5vw;
}
/* タブレット 2カラム */
@media screen and (max-width: 1024px) {
	.bl_media_itemWrapper {
		width: calc(100% / 2 - 30px);
	}
}
/* スマホ 1カラム*/
@media screen and (max-width: 599px) {
	.bl_media_itemWrapper {
		width: calc(100% / 1 - 30px);
	}
}

今回CSSの命名規則はPRECSSを参考にしています(我流入ってますが)。内容はこちらの名著で勉強できるので、コーディング勉強中の方は一度購入して読んでおくことをお勧めします。

カード型レイアウトのCSS解説

<div class="bl_media_container">
    <div class="bl_media_itemWrapper">
      <div class="bl_media_item"></div>
    </div>
<!-- ...以下必要なアイテム数分記述 -->
</div>
  • media_container・・・全体の枠
  • media_itemWrapper・・・カードの大きさやmarginを指定する枠
  • media_item・・・カード自体のスタイルを当てるクラス

ここで重要なのはmedia_containerとmedia_itemWrapperの扱い方です。

今回カラムは3カラムで、アイテム間の余白は上下左右30pxの設定にします。

.bl_media_container {
	display: flex;
	flex-wrap: wrap;
	padding: 30px;
}

media_containerは全体の枠となるので、ここではflex-wrapを指定しておきます。あと構成には関係ないですが見やすいように一応paddingも指定してます。

.bl_media_itemWrapper {
	width: calc(100% / 3 - 30px);
	margin: calc(30px / 2);
}

media_itemWrapperは各アイテムに対するレイアウトを指定するクラスです。

まずmarinから説明すると、margin: calc(30px / 2)で上下左右に15pxの余白を作っています。

隣のアイテムの余白と合計することで30pxになります。

Image from Gyazo

わざわざ30px / 2にしたのは「隣り合わせた合計が設けたい余白である」ということを分かりやすくするためだけなので、直接15pxと書いてしまっても問題はありません。

次にwidthですが、こちらは100%をカラム数3で割った数値から、marginの余白を引いています。

nthを使った方法と違い、今回は一方向ではなく上下左右にmarginをあてているので、余白は下の図のような感じになっています。

上下左右にmarginがあたっている図

左右のmargin合計が(15+30+30+15)で90pxになるので、これをカラム数3で割った「30px」を引いているということです。

ただ、枠内に沿って配置されている要素含全ての要素の上下左右にmarginがあたっているので、図だと分かりづらいですが、コンテナ内側に余白が生まれてしまっています。

ネガティブマージン設定前

これを消すためにコンテナ要素にネガティブマージンを指定します。

.bl_media_container {
	display: flex;
	flex-wrap: wrap;
    margin: calc(-30px / 2); /* ←追記 */
	padding: 30px;
}

アイテムに指定したmarginと同じ値にマイナス指定します。

これで上下左右15px分内側の余白を消すことができます。

デベロッパールールだとネガティブマージンが色で表示されないので分かりづらいですが、コンテナ要素のpadding30pxに沿った位置にぴったりついてます。

Image from Gyazo

これでカードレイアウトの完成です!

この指定方法は、メディアクエリなどでカラムが切り替えるデザインの場合に効果を発揮します。

余白は変えずに2カラムにしたい場合は、

@media screen and (max-width: 599px) {
	.bl_media_itemWrapper {
		width: calc(100% / 2 - 30px);
	}
}

これだけでOKです。nthで指定する方法と比べるとめちゃくちゃ簡単で、アイテム幅を100% / 3 から 100% / 2 にするだけです。

SCSSのコード

ここからはおまけですが、SCSSの場合のコードも用意しました。

考え方は上と同じですが、変動するカラム数や余白を変数にまとめています。(mixinでまとめるともっと簡略化できます)

* {
	list-style: none;
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}
img {
	width: 100%;
	vertical-align: bottom;
}
/***************************/
/* 変数 */
$interval: 60px; /* カード間の余白 */
$number: 3;      /* カード表示枚数 */
/***************************/
.bl_media_container {
	display: flex;
	flex-wrap: wrap;
	margin: $interval / -2;
	padding: 20px;
}
.bl_media_itemWrapper {
	width: calc(100% / #{$number} - #{$interval});
	/* [100% / 表示させたいカード枚数]  -  [上下左右の枠にかかるマージン] */
	margin: $interval / 2;
	/* カード間の余白(隣接要素との合計になるため1/2を指定) */
}
.bl_media_item {
	outline: 1px solid #000;
	font-size: 1.5vw;
}
// 599px以下
@media screen and (max-width: 599px) {
	/***************************/
	/* 変数 */
	$interval: 30px; /* カード間の余白 */
	$number: 2;      /* カード表示枚数 */
	/***************************/
	.bl_media_container {
		margin: $interval / -2;
	}
	.bl_media_itemWrapper {
		width: calc(100% / #{$number} - #{$interval});
		margin: $interval / 2;
	}
}
// 480px以下
@media screen and (max-width: 480px) {
	/***************************/
	/* 変数 */
	$interval: 40px; /* カード間の余白 */
	$number: 1;      /* カード表示枚数 */
	/***************************/
	.bl_media_container {
		margin: $interval / -2;
	}
	.bl_media_itemWrapper {
		width: calc(100% / #{$number} - #{$interval});
		margin: $interval / 2;
	}
}

これで管理することで、コーディング後にデザイナーさんから「やっぱ余白をちょっと広げたい」「スマホの時も2カラムで」なんて変更が入った場合も即時で修正可能です!

Image from Gyazo

CSSを変数に格納した場合のコード

IE非対応なのでまだ使うことは少ないかもしれないですが、SCSS同様、変数管理することで管理が強烈に楽になります。

* {
	list-style: none;
	box-sizing: border-box;
	margin: 0;
	padding: 0;
}
img {
	width: 100%;
	vertical-align: bottom;
}
/***************************/
/* 変数 */
:root {
	--interval: 30px; /* カード間の余白 */
	--number: 3;      /* カード表示枚数 */
}
/***************************/
.bl_media {
	background: #eff3fc;
}
.bl_media_container {
	display: flex;
	flex-wrap: wrap;
	margin: calc((var(--interval) / -2));
	padding: 30px;
}
.bl_media_itemWrapper {
	width: calc((100% / var(--number)) - var(--interval));
	/* [100% / 表示させたいカード枚数]  -  [上下左右の枠にかかるマージン] */
	margin: calc((var(--interval) / 2));
	/* カード間の余白(隣接要素との合計になるため1/2を指定) */
}
.bl_media_item {
	outline: 1px solid #000;
	font-size: 1.5vw;
}
@media screen and (max-width: 599px) {
	/***************************/
	/* 変数 */
	:root {
		--interval: 30px; /* カード間の余白 */
		--number: 2;      /* カード表示枚数 */
	}
	/***************************/
}
@media screen and (max-width: 480px) {
	/***************************/
	/* 変数 */
	:root {
		--interval: 40px; /* カード間の余白 */
		--number: 1;      /* カード表示枚数 */
	}
	/***************************/
}

まとめ

いかがでしたでしょうか?

記述が楽で使い回しもしやすく、ここにSCSSの変数管理を加えると保守性も高まります。

本当はこういった縦横方向のレイアウトなら、display:gridを使いたいところですが、IEのこと考えると….なので、しばらくはこの書き方でいきたいと思います。

また、当記事を読まれている方の中にはWeb制作初学者の方もいるかと思います。デザインやコーディングの基礎知識を学びたい方向けの記事を用意しているので是非見ていってください!

PENGIN無料コーディング課題

【デザインカンプ無料配布】未経験からのコーディング学習ステップ

オススメ書籍紹介

【初学者向け】書籍でWeb制作を学ぶ!オススメ書籍をジャンル別に紹介! Web制作のおすすめ本24選!(コーディング・Webデザイン)

オススメUdemy講座紹介

Udemyオススメ講座まとめ 【2021年8月】Udemy×Web制作!オススメ優良講座を紹介!

それでは今回はこのへんで!