🍱
CSS Grid Layout を使って複雑なレイアウトを綺麗に書きたい
以下のようなレイアウトを持つ画面を作成しようとした場合、アイテムの配置に CSS Flexbox を利用するよりも、CSS Grid Layout を利用する方が作りやすいと思います
また、以下のように条件によって異なるレイアウトを求められる場合、CSS in JS のフレームワークを用いれば、綺麗に書けます
今回使っている技術は以下です
- React v16.12.0
- Emotion v10.0.27
実装
コード全体は以下です
import React from "react";
import { css } from "@emotion/react";
import styled from "@emotion/styled";
const borderStyle = css`
border: 0.5px solid #ddd;
`;
const ContainerStyle = styled.div<{ type: "A" | "B" }>`
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-areas: ${(p) =>
p.type === "A"
? `'title title title'
'item00 item01 item01'
'item10 item11 item12'
'footer footer footer'`
: `'title title title'
'item00 item01 item01'
'item10 item10 item10'
'footer footer footer'`};
text-align: center;
${borderStyle}
`;
const TitleStyle = styled.h3`
grid-area: title;
${borderStyle}
`;
const ItemStyle = styled.div<{ i: number; j: number }>`
grid-area: ${(p) => `item${p.i}${p.j}`};
${borderStyle};
`;
const FooterStyle = styled.div`
grid-area: footer;
${borderStyle};
`;
export const Component: React.FC<Props> = ({ type, items }) => (
<ContainerStyle type={type}>
<TitleStyle>
<div>タイトル</div>
</TitleStyle>
{items.map((gs, i) =>
gs.map((s, j) => (
<ItemStyle key={s.id} i={i} j={j}>
<div>{s.name}</div>
</ItemStyle>
))
)}
<FooterStyle>
<div>フッター</div>
</FooterStyle>
</ContainerStyle>
);
それぞれ説明します
全体的なスタイル
grid-template-areas
を利用し、名前付きのグリッドテンプレートを定義します
Emotion を使って、パラメーターtype
を受け取り、利用するテンプレートを決定します
const ContainerStyle = styled.div<{ type: "A" | "B" }>`
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-areas: ${(p) =>
p.type === "A"
? `'title title title'
'item00 item01 item01'
'item10 item11 item12'
'footer footer footer'`
: `'title title title'
'item00 item01 item01'
'item10 item10 item10'
'footer footer footer'`};
text-align: center;
${borderStyle}
`;
レイアウトの追加があっても、テンプレートと条件を追加で対応できるし、レイアウトの変更もテンプレートを直すだけでおそらく対応可能です
ただし、このコンポーネントを一般化する時には、テンプレート定義は切り離して props 経由で渡される方がいいかもしれません
各要素のスタイル
grid-area
でテンプレートと紐付けます
ItemStyle は値が複数あり、ループを使って書きたいので map のインデックスを名前として使います
const TitleStyle = styled.h3`
grid-area: title;
${borderStyle}
`;
const ItemStyle = styled.div<{ i: number; j: number }>`
grid-area: ${(p) => `item${p.i}${p.j}`};
${borderStyle};
`;
const FooterStyle = styled.div`
grid-area: footer;
${borderStyle};
`;
Grid Layout を利用するケースってあまり今までなかった(Flexbox で全部やってた)のですが、調べると色々な機能があって便利だなと思いました
Discussion