見出し画像

CSS グリッドレイアウト チョットデキルようになった話

こんにちは。フロントエンドエンジニアの原口です。

さて、今回はCSS のグリッドレイアウトについてのお話です。

グリッドレイアウト使っていますか?
自分は勉強しなくてはなーと思いつつも使用場面がイマイチ思いつかなかったことや、flex使えれば良くない??という考えがあったため、なかなか手を付けられずにいました。

しかし、他の方が使用しているコードを見て、こんなことできるんだ!こういう使い方もできるのね!状態になったため、今回はそちらを紹介したいと思います!

グリッドレイアウトを使用するために

まずはグリッドレイアウトを使用するための設定を行いましょう!
といってもとても簡単です。

グリッドレイアウトは、グリッドレイアウトを適用したい要素を囲う要素にグリッドレイアウトの指定を行うことで、その要素をグリッドコンテナーに、子要素をグリッドアイテムにすることができます。

以下の例では、<GridContainer>に対し、display: gridを指定したことにより、<GridContainer>がグリッドコンテナーに、<GridItem>がグリッドアイテムになりました。

<GridContainer>
  <GridItem>Grid Item</GridItem>
  <GridItem>Grid Item</GridItem>
  <GridItem>Grid Item</GridItem>
</GridContainer>

const GridContainer = styled.div`
  display: grid;
`
const GridItem = styled.div`
  ...
`

実際に表示されているコンポーネントがこちらです。

まだこの段階では、指定をしていない状態と見た目に変化はありませんが、これでグリッドレイアウトが使用できるようになりました。
(ここではわかりやすくするために、ボーダーと背景色を敷いています。)

ちなみにグリッドアイテムをインライン要素にしたい場合は、display: inline-gridが使用できます。

const GridContainer = styled.div`
  display: inline-grid;
`

これで準備はOKです。

ここからは実際に自分がグリッドレイアウトの便利さを感じたものや、そういう使い方があるのかーと勉強になったコードを紹介していきたいと思います。

1. gap

gap はグリッドアイテムの行や列の隙間を定義するプロパティです。
以下のようにグリッドコンテナーに対し、指定したい余白を指定します。

const GridContainer = styled.div`
  display: grid;
  gap: 20px;
`

これで要素の間に 20px の余白ができました。

Grid Itemと書かれた要素の間にある紫色のスペースがgapで指定した余白になります。
marginとは異なり余白ができるのは要素間だけになります。

今までgapを使っていない時は、<GridItem>に対して以下のようなスタイルを指定を行っていました。

// 1. 兄弟要素を使用
const GridItem = styled.li`
  & + & {
    margin-top: 20px;
  }
`

// 2. 否定擬似クラスを使用
const GridItem = styled.li`
  :not(:first-child) {
    margin-top: 20px;
  }
`

しかしグリッドレイアウトでは親要素であるグリッドコンテナーに gapを追加するだけでレイアウトを組めるためとてもシンプルに書くことができます。

また、先日同じFEチームの佐伯さんが書いた記事にあるとおり、最近スペースマーケットではChakra-UIを採用することになったのですが、Chakra-UIとの親和性が高いと個人的には感じています。
以下の例では<HogeList>に対しgridとgapを指定しています。そうすることで、子要素のコンポーネントのスタイルを確認しなくてもリスト間の余白が16pxであることがわかります。

// <HogeListItem>のスタイルを確認しなくても、<HogeListItem>間は16pxの余白があることがわかります
<HogeList d="grid" gap="16">
  {hogeList.map(item => (
    <HogeListItem>{item.name}</HogeListItem>
  ))}
</HogeList>

ちなみにgapという指定は、row-gapとcolumn-gapのショートハンドになります。
そのため以下の指定では、どちらでも見た目が同じになります。

const GridContainer = styled.div`
  display: grid;
  gap: 20px 5px;
`

const GridContainer = styled.div`
  display: grid;
  row-gap: 20px;
  column-gap: 5px;
`

2. 横並びレイアウト

2つ目は、以下のような横並びレイアウトについてです。

このような並びを作るためには、grid-template-columnsプロパティを使用します。

const HorizontalList = () => {
  return (
    <Horizontal>
      <HorizontalItem>新宿</HorizontalItem>
      <HorizontalItem>渋谷</HorizontalItem>
      <HorizontalItem>池袋</HorizontalItem>
    </Horizontal>
  );
};
const Horizontal = styled.div`
  display: grid;
  gap: 20px;
  grid-template-columns: 1fr 1fr 1fr;
`;
const HorizontalItem = styled.div`
  ...
`;

grid-template-columnsの値には、列の数だけ幅の指定を追加します。
今回は3つの要素を横並びにさせたいため、1frを3回繰り返します。

このfrという単位はグリッドレイアウトを使用する際に使用できる単位で、fraction(分数)の略だそうです。
frについては言葉で説明するのが難しいのですが、考え方としては、grid-template-columnsの値で指定したfrの合計を分母とし、分子のサイズはfrに指定した値分が要素のサイズとなるイメージでしょうか。

…よくわからないと思うので、以下の例をご覧ください。

const HorizontalList = () => {
  return (
    <Horizontal>
      <HorizontalItem>新宿</HorizontalItem>
      <HorizontalItem>渋谷</HorizontalItem>
      <HorizontalItem>池袋</HorizontalItem>
    </Horizontal>
  );
};
const Horizontal = styled.div`
  display: grid;
  gap: 20px;
  grid-template-columns: 2fr 1fr 1fr;
`;

grid-template-columnsで指定しているfrの合計は、2fr + 1fr + 1fr = 4frなので分母は4になり、1つ目要素(新宿)は2frが指定されているので、widthがグリッドコンテナーに対し2/4のサイズになります。
上のコードを指定した際の画面は以下のようになります。

また同じ値で繰り返し指定を行う場合は、repaet関数を使用することでスマートに書くことができます。

const Horizontal = styled.div`
  display: grid;
  gap: 20px;

  // 第一引数で何回繰り返すかを指定し、第二引数で繰り返す要素の単位を指定する
  grid-template-columns: repart(3, 1fr);
    => grid-template-columns: 1fr 1fr 1fr; と同義
`;

これですっきり書けるようになりました!

ちなみにgrid-template-columnsで指定する値は、pxやcalc関数を使用することもできます。

const Horizontal = styled.div`
  grid-template-columns: repart(3, 50px);
  grid-template-columns: repart(3, calc(10px + 100px));
`;

個人的に、横並びのレイアウトについては以下の使い分けが良さそうだと感じました。

grid
子要素の数が固定の場合。
grid-template-columnsで繰り返す回数を指定するため、子要素があるだけ横並びにしたい、といったような回数を指定できない/指定する数が多い場合は扱いづらいのかなと思いました。(回数を指定しなくてもいい方法があったらすみません!🙏)
だったらはじめからflexでも良さそうですが、gapを使用した余白のサイズ指定はやはり魅力的なため、子要素の数が固定であればgridを使っていくのが良さそうと感じました。

flex
子要素の数が固定ではない場合。
gridのところでも書いていますが、子要素を全て横並びにしたい場合は、やはりflexの方が楽かなと思いました。

このあたり、まだ自分の実装経験が浅いため、誤りがあればご指摘いただけるとありがたいです🙏

3. 画像 + テキストの横並びコンポーネント

3つ目は、画像 + テキストの横並びコンポーネントです。
見た目としては以下のようなコンポーネントで、こちらは実際にメンバーのコードレビューをしていて、こんな使い方もできるのか!と発見があったものです。

こちらも2で説明したgrid-template-columnsを使用しており、以下のように指定することで画像が200px、テキストがなりゆきになるようなコンポーネントが作成できます。

const GridCard = () => {
  return (
    <GridWrapper>
      <GridImageWrapper>
        <img src="https://via.placeholder.com/200x100" />
      </GridImageWrapper>
      <GridTextWrapper>
        <dt>タイトル</dt>
        <dd>テキストテキストテキストテキストテキストテキストテキスト</dd>
      </GridTextWrapper>
    </GridWrapper>
  );
};
const GridWrapper = styled.div`
  display: grid;
  grid-template-columns: 200px 1fr;
`;
const GridImageWrapper = styled.div`
  ...
`;
const GridTextWrapper = styled.dl`
  padding-left: 16px;
  ...
`;

以上になります!

最後に

いかがだったでしょうか?
グリッドレイアウトでできることはまだまだあるので、本当の意味でちょっとできる程度の紹介になりましたが、この記事を読んで少しでもグリッドレイアウト チョットデキルようになってくれれば幸いです!

最後に宣伝です。
スペースマーケットでは現在メンバーを募集しております!

われこそは!という方がいらっしゃいましたら、お話だけでも結構ですので、ぜひお話できればと思います!


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