複数の name-value リストで name 列の幅を CSS だけで動的に揃える

# TL;DR

複数の dl 要素による name-value リストにおいて、name 列（dt 要素）の幅を CSS だけで動的に（幅を決め打ちしないで）揃える方法です。

グリッドレイアウト `display: grid` と `display: contents` で実現できます。ただし、2018 年 9 月時点で [IE 11 と Edge は後者をサポートしていません](https://caniuse.com/#feat=css-display-contents)。

お急ぎの方は [CodePen](https://codepen.io/kazuhiro2nd/pen/zmxqaO) をご覧ください。

# 背景

Web アプリの UI を実装していると、よく次のような name-value リストが必要になることがあります。

<img width="704" alt="name-value リスト" src="https://qiita-image-store.s3.amazonaws.com/0/120226/04d0a937-5330-c60b-5f82-9ed414e34c06.png">

このようなリストは HTML では通常 dl, dt, dd 要素を用いて次のようなマークアップを行います。

```html
<div class="container">
  <header>
    <h1>User Information</h1>
  </header>
  <div class="content">
    <dl>
      <dt>Username</dt><dd>john-smith</dd>
    </dl>
    <dl>
      <dt>Full name</dt><dd>John Smith</dd>
      <dt>Age</dt><dd>20</dd>
      <dt>Email address</dt><dd>john-smith@example.com</dd>
    </dl>
    <dl>
      <dt>Type</dt><dd>Admin</dd>
      <dt>Language</dt><dd>English</dd>
    </dl>
  </div>
</div>
```

ここで、上図の破線のように、name 列（dt 要素）の幅を揃えたいとします。全ての行が一つの dl 要素内であれば、`display: table` などを用いて比較的簡単に実装できます。

しかし、今回は複数の dl 要素からなっています。こういった場合にリストをまたいで name 列の幅を揃えるにはどうすればよいでしょうか。この記事では、その具体的な方法を書きます。

なお、複数の dl 要素を使っているのは、リストを意味ごとにグループ分けしたいという目的があるので、スタイルのためにマークアップを変更するのは前提として避けます。

# グリッドレイアウト + `display: contents`

方法としては、グリッドレイアウト `display: grid` と `display: contents` を用います。スタイルシートは次のような感じになります（重要な部分のみ抜粋）。

```css
.content {
  display: grid;
  grid-template-columns: max-content 1fr;
}

dl {
  display: contents;
}
```

## リストのコンテナでグリッドを定義

まず、複数の dl 要素を持つコンテナ（今回は `<div class="content">`...`</div>`）で `display: grid` とし、`grid-template-columns` で列の幅を定義します。

ポイントは一列目の **`max-content`** で、これは要素の中身に合わせて幅が最適化されるという非常に便利な値です。今回のケースでは、dt 要素の中のテキストが改行されないように、かつ余りが出ないようにピッタリな幅を自動的に計算してくれます。

`max-content` は CSS Intrinsic & Extrinsic Sizing Module Level 3 で追加されました。より詳細を知りたい方は [Intrinsic Size Determination](https://www.w3.org/TR/css-sizing-3/#intrinsic) セクションをご覧ください。

これで、以下のようなグリッドレイアウトで表示されるようになります。

<img width="694" alt="グリッドレイアウト" src="https://qiita-image-store.s3.amazonaws.com/0/120226/ebe0b010-fd6c-eabc-0ee9-717a86a1421a.png">

でも崩れてしまっていますね…。それもそのはず、定義した各グリッドの中に入るのは、`display: grid` にした要素の**直下の要素**だからです（「グリッドアイテム」と呼ばれます）。直下にあるのは dl 要素なので、リストごと各グリッドの中に放り込まれている状態です。

## `display: contents` でその要素を “無かったことに” する

そこで登場するのが、**`display: contents`** です。これは、指定した要素を “無かったことに” します。これを先ほどの dl 要素に対して指定することで、dl 要素は無かったこととしてレイアウトされ、その中の dt, dd 要素が `display: grid` にした要素の直下に来るようになります。

よって、以下のようなレイアウトになり、作りたかったものができました。

<img width="694" alt="display: contents を用いたあとのレイアウト" src="https://qiita-image-store.s3.amazonaws.com/0/120226/7e923d86-0432-c68b-c600-0c3ae8cffb9f.png">

`display: contents` のイメージとしては、**それを指定した要素だけが `display: none` になる**ことかなと思います。`display: none` はその要素自身とその配下の要素すべてが生成されなくなりますが、`display: contents` は自身だけが生成されず、その配下の要素は通常通り生成されます。

`display: contents` は CSS Display Module Level 3 で定義されています。詳細は [Box Generation: the ‘none’ and ‘contents’ keywords](https://www.w3.org/TR/css-display-3/#box-generation) セクションをご覧ください。

また、`display: contents` の用途などについてはこちらの記事 [“More accessible markup with display: contents”](https://hiddedevries.nl/en/blog/2018-04-21-more-accessible-markup-with-display-contents) がわかりやすいです。

# 対応ブラウザ

2018 年 9 月時点での各機能の実装状況です。**IE 11 と Edge は対応していない**のでご注意ください。

## `max-content`

[Can I use `max-content`?](https://caniuse.com/#feat=intrinsic-width)

<img width="1272" alt="スクリーンショット 2018-09-29 14.50.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/120226/4d0eb471-a248-61b3-4721-546c252f92b5.png">

## `display: contents`

[Can I use `display: contents`?](https://caniuse.com/#feat=css-display-contents)

<img width="1272" alt="スクリーンショット 2018-09-29 14.50.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/120226/df7a43a1-a3ba-9599-4cd6-ee42998cf971.png">

# ソースコード・デモ

[CodePen](https://codepen.io/kazuhiro2nd/pen/zmxqaO) でご覧いただけます。

# 参考

- [CSS Intrinsic & Extrinsic Sizing Module Level 3 — W3C Working Draft, 4 March 2018](https://www.w3.org/TR/css-sizing-3/)
- [CSS Display Module Level 3 — W3C Candidate Recommendation, 28 August 2018](https://www.w3.org/TR/css-display-3/)
- [More accessible markup with display: contents](https://hiddedevries.nl/en/blog/2018-04-21-more-accessible-markup-with-display-contents)
- [CSS Grid でテーブルを作る](http://var.blog.jp/archives/75162625.html)