見出し画像

CSSレイアウトパターン(5)要素をマス目状に敷き詰める

これだけは身に付けてほしい、6つのCSSレイアウトパターン」の詳細解説です。5つ目は一覧ページでの利用機会が多い「要素をマス目状に敷き詰める」です。「マス目状に敷き詰める」と表現しましたが、技術用語で「グリッド」と言います。

このパーツは、デザイントレンド、デザイン法則についても、少し解説する必要があります。また、敷き詰めている要素(この例では画像とボタンが付いたカード)の体裁にも気を配る必要が出てきます。

私の実務経験に基づいているので #この経験に学べ に参加しています。

はじめての方はお読みください↓↓

https://note.com/webbingstudio/n/nd2c3682dfc0c

サンプル

実際にブラウザで見られるサンプルはこちらです。

このパターンが使用されるパーツ

同じ形をした繰り返し要素(ブログ記事やスポット情報など)を、画面内にたくさん表示する際に使用します。繰り返し要素は、画像・見出し・ボタンが付いた、四角いカードの形をしていることがほとんどです。
情報量が多いポータルサイト、新聞社などのニュースサイトで頻繁に使用されています。

トレンドの変化

デザインは勉強しておらず、作り方だけを知りたい人は、この項は読み飛ばしてください。

長年、定番とされてきたこのパーツですが、最近は少し古いデザインとなっています。2022年末の時点で、多くのポータルサイトでは以下のようなパターンに置き換わっています。一般に「カルーセル」と呼ばれます。

  • 記事一覧を地域別、カテゴリー別などに分けて複数表示する

  • 一覧を左右一列で表示する

  • 隠れているものは、出てくるのを待つか、左右にスワイプ(ドラッグ)して見る

食べログトップページ「百名店」

「古いのなら、もう覚えなくてもよいのでは?」
いいえ、そんなことはありません。カルーセルは作成にJavaScriptの知識が必要となるため、以下の理由で採用せず、敢えてグリッドにすることもあります。

  • 制作者の技術、予算の不足

  • 官公庁サイトなど、アクセシビリティ※を優先する場合

  • サイトを立ち上げたばかりで、カテゴリー別にするほど記事がない場合

つまり、デザイン重視のサイトで提案するのは避けるべきですが、まだまだ出番は多いと思ってください。

※アクセシビリティとは、体の障害、閲覧環境に依存しない、あらゆる人への利用のしやすさのことです。カルーセルは隠れて見えない記事があったり、キーボードだけでは操作しづらいため、閲覧に不自由を感じる人が出てきます

基本的な挙動

特に指示がなかったり、おまかせ or 自分がデザイナーであれば、以下のような挙動が基本です。

  • 各列の要素の高さは、一番高い(内容の行数が多い)ものに揃える

  • ウィンドウ幅が広いときは一覧の最大幅を制限する

  • 最終行で列数が足りなかった場合は左に揃える

並び順は左上から右下です。CSSで変えることはできますが、普遍のルールなので、よほど変化を求めない限り守ってください。

ユーザーは普段よく利用していて馴染みのあるプロダクトと同じような挙動を、他のプロダクトにも期待する

【知っておきたい】UI UXデザインに活用できる15の法則・ルール!具体例を交えて解説 | 株式会社ニジボックス

という法則があります(ヤコブの法則)。

さらにこのパターンは、画面幅によって列数が変わります。一般には以下のとおりです。

  • スマートフォンでは50%×2列で並べる

  • タブレット縦(640px以上、768px以上)では33%×3列で並べる

  • タブレット横、デスクトップ(1024px以上)では25%×4列で並べる

HTML

リスト全体を囲んでいる要素と、その中の多数の繰り返し要素で構成されています。繰り返し要素は「そのパーツの英語名__card」や「そのパーツの英語名__item」というクラス名がよく使用されます。

多くの制作会社が採用しているBEM記法だと、以下の通りになります。.sample__card は、article要素でマークアップされることが多いです。

<div class="sample">

    <article class="sample__card">
        <!-- ここに画像やテキストが入る -->
    </article><!-- /.sample__card -->

    <article class="sample__card">
        <!-- ここに画像やテキストが入る -->
    </article><!-- /.sample__card -->

    <article class="sample__card">
        <!-- ここに画像やテキストが入る -->
    </article><!-- /.sample__card -->

    <article class="sample__card">
        <!-- ここに画像やテキストが入る -->
    </article><!-- /.sample__card -->

</div><!-- /.sample -->

WordPressの公式テーマやブロックで使用されているSMACSS記法だと、ハイフン区切りなので以下の通りになります。

<div class="sample">

    <article class="sample-card">
        <!-- ここに画像やテキストが入る -->
    </article><!-- /.sample-card -->

    <article class="sample-card">
        <!-- ここに画像やテキストが入る -->
    </article><!-- /.sample-card -->

    <article class="sample-card">
        <!-- ここに画像やテキストが入る -->
    </article><!-- /.sample-card -->

    <article class="sample-card">
        <!-- ここに画像やテキストが入る -->
    </article><!-- /.sample-card -->

</div><!-- /.sample -->

なお、ここでは .sample__card に、さらに .card クラスを付与している前提で解説します。
.sample__card にはグリッドのマス目のスタイル、 .card には記事の枠線や文字サイズなどのスタイル、と分けて記述した方が長期的に見て管理しやすいからです。

<div class="sample">

    <article class="sample__card card">
        <!-- ここに画像やテキストが入る -->
    </article><!-- /.sample__card -->

</div><!-- /.sample -->

.cardのHTML

.card の中身「<!-- ここに画像やテキストが入る -->」のHTMLのサンプルを挙げておきます。
.cardには画像・見出し・ボタンがあります。見出しの文字数はそれぞれ異なり、複数行になるかもしれません。 CodePenのサンプルも参照してください。

<article class="sample__card card">

    <div class="card__image">
        <img src="(画像のURL)" width="(画像の本来の幅)" height="(画像の本来の高さ)" alt="">
    </div><!-- .card__image -->

    <div class="card__contents">
        <p class="card__body">(何行になるかわからない見出し)</p>
    </div><!-- /.card__contents -->

    <div class="card__footer"><a href="#" class="card__button">(ボタン)</a></div>

</article><!-- /.sample__card -->

CSS

flexboxとCSS Gridの二通りの作成方法がありますが、実際のサイトで採用例が多い、flexboxで解説します。

.cardのCSS

先に .card のCSSを挙げておきます。カードには薄い影が入っていて、浮いているように見えます。 CodePenのサンプルも参照してください。

.card {
    padding: 10px;
    background-color: #fff;
    border-radius: 4px;
    box-shadow: 0 0 6px rgba(0,0,0,0.25);
}

.card__image {
    text-align: center;
}

.card__image img {
    width: 100%;
    height: auto;
    vertical-align: bottom;
}

.card__contents {
    margin-top: 15px;
}

.card__body {
    margin: 0;
    font-size: 0.75rem;
    line-height: 1.5;
}

.card__footer {
    margin-top: 30px;
}

.card__button {
    display: flex;
    align-items: center;
    justify-content: center;
    color: inherit;
    border: 1px solid #999;
    width: 100%;
    max-width: 100px;
    height: 1.5rem;
    margin-left: auto;
    margin-right: auto;
    font-size: 0.625rem;
    text-decoration: none;
    line-height: 1;
}

グリッド全体のスタイル

まず、グリッド全体である .sample の最大幅を制限しておきます。サイトのデザインにより異なりますが、1200px〜1920pxくらいでそれ以上広がらないようにします。
別途解説記事を書いているので、こちらを参考にしてください。

.sample に display: flex を指定して、flexboxとして扱います。この時点で、子要素= .sample__card が横一列に並んで画面からはみ出します。

.sample {
    display: flex;
}

続けて、 flex-wrap: wrap を指定します。
flex-wrapは子要素が画面幅に収まらなかったときの挙動です。 .sample__card が横一列になったのは、この初期値がnowrap(改行しない)となっているからです。

wrap(自動改行する)に変更すると、画面からはみ出していた .sample__card が整列するようになります。

.sample {
    display: flex;
    flex-wrap: wrap;
}

続けて、 align-items: stretch を指定します。
align-items はflexboxの「列」にあたる方向の揃え方です。初期値は上揃えですが、値を「stretch」にすると .sample__card がその列で一番高さがあるものに揃って伸び切るようになります。
意味がよくわからない場合は、.sample__card の見出し(.card__body)に、どれかひとつだけ大量のテキストを入れてみてください。

.sample {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
}

次に .sample__card 同士の余白を、gapプロパティで指定します。

.sample {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 15px;
}

グリッド内のマス目のスタイル

ここからはグリッドのマス目、子要素である .sample__card にスタイルを指定していきます。
まず flex-grow: 0 を指定します。

.sample__card {
    flex-grow: 0;
}

これを指定しても通常は何も起こりませんが、例えば横3列(33%)で並べたときに、最終行の記事が3個に満たなかった場合の挙動が変わります。
flex-growを0にしておかないと、例えば最終行が1個だったときに、幅が33%ではなく100%に伸び切ってしまいます。うっかり指定を忘れると、サイト公開後に修正になったりするので気をつけてください。

次に、box-sizing: border-box を指定します。これは .sample__card にpaddingが指定されている場合に、正しく幅の計算をさせるために必要となります。

.sample__card {
    flex-grow: 0;
    box-sizing: border-box;
}

次に flex-basis プロパティで「スマートフォン以下のときの幅」を指定します。ウェブ制作ではスマートフォン以下の見た目が基本で、タブレットやデスクトップの見た目はオプションとして扱います(モバイルファースト)。

2列ですから50%…ではありません。gapで指定した余白を引かなくてはなりません。長くなりますがcalc()を使用します。公開時点では引いてなかったのは内緒です。

.sample__card {
    flex-grow: 0;
    flex-basis: calc( 50% - ( 15px / 2 ) );
}

flex-basisは説明が難しいのですが「そうあるべき、成り行きの幅」です。MDNでは「主要部分の初期の寸法」と定義されています。widthとは違い、小要素の数や文字数によって幅が変わることがあります。

なお、このサンプルの場合は max-width: 50% でもほぼ同じ結果になります。

画面幅による段組の変更

最初に書いたとおり、タブレットでは3列、デスクトップでは4列としたいです。メディアクエリを追加して、以下のように幅を上書きします。calc()がどんどん複雑になります。

.sample__card {
    flex-grow: 0;
    flex-basis: calc( 50% - ( 15px / 2 ) );
}

@media screen and (min-width: 768px) {
    .sample__card {
        flex-basis: calc( 33.33333333% - ( 15px / 3 * 2 ) );
    }
}

@media screen and (min-width: 1024px) {
    .sample__card {
        flex-basis: calc( 25% - ( 15px / 4 * 3 ));
    }
}

これで、カードがきっちりマス目上に並び、ウィンドウ幅によって列数が変化するようになります。

幅と余白の指定については、もっと簡潔な方法があるのではないのかと考えた時期が私にもありました。しかし「基本的な挙動」で挙げた

最終行で列数が足りなかった場合は左に揃える

の条件を満たすのが難しいです。2023年の時点ではこれが一番コードが短いです。

補足:カードのボタンを下端に揃える

グリッド自体の解説はこれで終わりです…が、あなたがコーディングに自信があり、信頼されるウェブ制作者になりたい!と思っているなら、この項もぜひ読んでいってください。

カードの中にボタンがある場合は、少しややこしい問題があります。ボタンの位置です。

デザインカンプを渡されたときは、ボタンの位置に注目してください。おそらく、ボタンはカードの下の辺にピッタリ揃っているはずです。
しかし、カードがブログ記事などの場合は、その上にある見出しが何行になるか=カードがどのくらいの高さになるかが不定です。つまり、要素の高さに関係なくボタンを下に揃えなくてはならないのです。

ちなみにデザイナーさんはそこまで考えていないことも多いので、この対策をせずに納品すると、公開後に修正依頼が来ることがあります。

具体的な対策

この対策をするには、カード= .card にスタイルを追加する必要が出てきます。仕組みとしては下の図の通りです。カードの見出し=文章が入る箇所を行数に関係なく伸びるようにすると、最後にあるボタンが押し込まれて、下端に揃うことになります。

まず、 .card をflexboxとして扱い、flex-direction: column を指定します。
flex-directionはflexboxの子要素が並ぶ方向です。初期値はrow(横)ですが、columnにするとdisplay: blockを指定しているときと同じように縦並びになります。

.card {
    display: flex;
    flex-direction: column;
    align-items: stretch;

    padding: 10px;
    background-color: #fff;
    border-radius: 4px;
    box-shadow: 0 0 6px rgba(0,0,0,0.25);
}

display: block のときと違うのは、flex-glow プロパティを使用できる点です。
カードの見出し=文章が入る .card__contents に flex-grow: 1 を指定すると、見出しの行数に関係なく高さが伸び切るようになります。

.card__contents {
    flex-grow: 1;
}

意味がよくわからない場合は、.card__bodyのそれぞれの文字数を大幅に変えてみてください。上の図のとおりに、カードの高さに関わらず、ボタンは下にくっついているはずです。

カンプをHTMLで再現するだけではない

5つめのパーツの解説は、書くのにかなり苦労しました…
私が体調を崩して書くのをお休みしていたのもありますが、単なるグリッドの作成以外にも、即戦力のウェブ制作者になるなら知っておくべきことがたくさんあったからです。

  • 採用されなくなってきていること

  • アクセシビリティの面でまだまだ必要であること

  • カードの高さ=見出しの文字数が変化する場合

ウェブデザイン全般と、WordPressなどのブログツール(CMS)の知識が身に付いていないと、このあたりは理解が難しいかもしれません。ですが、ぜひ経験を重ねて自分でもどんどんコードを書いて、ただ「描いたカンプをHTMLで再現する」だけではないウェブ制作者を目指してください。

今回はここまで。
このブログが、皆さんに必要なくなることを願っています。

この記事が参加している募集

この経験に学べ

この記事が気に入ったらサポートをしてみませんか?