CSS Gridでオシャレタイル型レイアウト🐈

**祝！IEの呪縛から解き放たれて使えるCSS**
といえばGrid、それとobject-fitも
その辺を使ったお手軽でフレキシブルで凝った雰囲気になるレイアウトを紹介します

## 作ったもの

ランダムなサイズの画像を、ある程度元の画像の比率を反映して良い感じに並べてみた。
画像はunsplushというフォトギャラリーAPIを使って持ってきています。

![スクリーンショット 2023-11-15 134951.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304716/4d3c1d64-033c-3057-d3d2-31136602b2b4.png)

↓スクロールした動画あります
https://x.com/herishiro/status/1724651582065909835

## こういうのを作るのに必要なコード

- **JavaScript : 8行**
- **CSS property : 12行**

今回はNext.js使ってるけどJQueryでもVanila JSでも多分何でもできます。

## CSS Gridとは？

今更の説明ですが…
- 縦と横のラインを引いて画面上に行と列を作り、そこにHTML要素を配置出来るやつ
- Flexと違い、レスポンシブで要素の位置を複雑に入れ替えるなど自由度の高いレイアウトが可能

![CSS-Grid-Empty-Space.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304716/ce756cb0-ee6d-a872-0511-187f9ddea9db.png)

## 実際のコード

```typescript:Next.js + Emotion
import { css } from "@emotion/react";
import Image from "next/image";
import { useEffect, useState } from "react";
import { PhotoRes } from "../../../pages/api/unsplash";

export const ImageTileScreen = () => {
	const style = styler();
	// APIから取得した画像データ
	const [photos, setPhotos] = useState<PhotoRes["results"]>([]);

	useEffect(() => {
		const fetchImages = async () => {
			const res = await fetch("/api/unsplash");
			const photoRes: PhotoRes = await res.json();
			setPhotos(photoRes.results);
		};
		fetchImages();
	}, []);

	const getGridSpan = (width: number, height: number) => {
		const ratio = width / height;
		const isLandscape = ratio > 1;
		const isRect = ratio > 1.3;
		const column = isLandscape ? Math.ceil(ratio) : 1;
		const row = isLandscape ? (isRect ? 2 : 1) : Math.ceil(1.8 / ratio);

		return { gridColumn: `span ${column}`, gridRow: `span ${row}` };
	};

	return (
		<div css={style.container}>
			{photos.map((photo) => {
				return (
					<div
						key={photo.urls.regular}
						css={[style.card, getGridSpan(photo.width, photo.height)]}
					>
						<Image
							src={photo.urls.regular}
							alt={photo.alt_description}
							width={photo.width}
							height={photo.height}
						/>
					</div>
				);
			})}
		</div>
	);
};

const styler = () => {
	return {
		container: css({
			position: "relative",
			display: "grid",
			gap: 10,
			gridTemplateColumns: `repeat(3, 1fr)`,
			gridAutoRows: "1fr",
			gridAutoFlow: "dense",
			padding: "0 5%",
		}),
		card: css({
			position: "relative",
			borderRadius: 10,
			overflow: "hidden",
			span: {
				height: "100% !important",
				img: {
					objectFit: "cover",
				},
			},
		}),
	};
};
```


## 仕組み

1. 画面全体にGrid layoutで３列のgridを引く
2. 各画像の縦横サイズを比較して、横長の画像なら１行×縦比率分のn列、縦長の場合は横比率分のn行×１列のグリッドを割り当てるcssを追加する。
（一定以上正方形に近い場合は2行×2列にする）
3. **隙間を上手いこと埋めるのはCSSが勝手にやってくれる！**

## 素敵な新入りのプロパティたち

### `gap: <マージンサイズ>`

コンテナ要素に指定して要素”間”のマージンを指定出来る。

従来のmargin-rightでは必要だった最後の要素には付けないなどの工夫が不要になる！

なお2列の要素の縦の間隔にも使える。flexでも使える。

![IGlZi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304716/0c945c76-0abb-d478-a465-03efaafa3229.png)

### **`grid-template-columns: repeat(<繰り返し回数>, <列の幅サイズ>)`**

コンテナ要素に指定して、`repeat()`内の第1引数に**列の数**、第2引数に**各列のサイズ**を渡すことで等間隔のgridの列を引くことが出来る。

今回第二引数に渡している”1fr”の`fr`は、固定で指定した幅やマージンを除いた部分をよしなに埋めてくれるgridと一緒に登場した便利な単位。

```tsx
gridTemplateColumns: `repeat(3, 1fr)`,
```

よってこの指定の意味は以下になる

**画面幅に関わらず、gapやpadding, marginを除いた幅を3等分した位置で画面を分割する。**

## **`grid-auto-flow: dense`**

要素ごとのサイズにばらつきがある時に、なるべくコンテナ内に綺麗に並ぶようにしてくれる。

ご覧のように限界はあるが、なるべく左上に向けて要素を詰めて表示されている。

**※要素の順番はバラバラになるので注意！**

![スクリーンショット 2022-09-02 232512.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304716/c99dc217-7abf-53b8-7f51-28f1e9df806d.png)

### `object-fit: cover;`

**object-fit**を画像を囲んだ要素に指定することで、画像のサイズや縦横比がどこを基準にするか決められる

`cover`は縦横比を保ちつつ、要素内に余白が出ないようにサイズを調整する（＝縦と横で幅が広い方の長さに合わせて、要素に入りきらない部分は枠外にはみ出す）

今回は外部APIから取得した画像なので使えなかったけど、この辺の操作はbackground-imageで指定して、その関連プロパティを使った方が扱いやすい気もする

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304716/48719a42-fb3f-6e84-afd7-314707b65c0b.png)

## 余談

gridを20列にして、細かく縦横比を指定したらもっとカッコ良くなるかと思ったら、`grid-auto-flow: dense`が上手くかみ合わなくてむしろ見た目が悪くなってしまった＞＜

![スクリーンショット 2022-09-02 234541.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304716/3decef04-85c1-c03b-b4bb-991247adc4af.png)