キャプションの幅を画像の幅に合わせる

Fig 1: Wikipedia のキャプションつき画像の例

ちょっとわかりにくいタイトルになってしまいましたが、つまり Wikipedia のやつ (Fig 1) みたいに、「画像 (またはビデオなどの埋め込みコンテンツ) の下にキャプションをレイアウトするとき、キャプションが複数行にわたる場合は画像の幅に合わせて折り返したい (ただし画像の幅はバラバラ)」というのをシンプルかつ汎用的なかたちで実現する CSS を考えてみました。というか、このブログのレイアウト用にけっこう長いことあれこれ考えてたもののこれといった手が思いつかなくて放置していたんですが、上手い解決を Stack Overflow でみつけた のでパクった、という話です。

<figure>
    <img src="/img/photo.jpg">
    <figcaption>Lorem ipsum dolor sit amet</figcaption>
</figure>

ここではこのマークアップをもとに考えてみます。スタイルの要件をまとめると以下のとおり:

考えられるアプローチのひとつとして、figure の幅についてあらかじめいくつかのパターンを決めておくというものがあります。imgfigure の幅に合わせて書き出してもいいし、大きめに作っておいてブラウザで縮めてもいいでしょう。シンプルなグリッドがきっちり決まってる場合なんかはいいかもしれませんが、マークアップの class 属性や data-* 属性などを指定する必要があり、あまり汎用的とは言えないかもしれません。

figure.small   { width: 160px; }
figure.mideium { width: 240px; }
figure.large   { width: 320px; }

figure img { max-width: 100%; }

あるいは JavaScript で img の幅を取得して figure の幅を操作するという手もあります。これならかなり汎用性も高いです。ただ、JavaScript で画像のサイズの取得をエラー処理やなんかも含めてきっちりやるのって、けっこうたいへんですよね? 僕は何度か挫折しました…

あとは CMS とかのシステム側でうまいことやってもらうって感じですかね。画像のアップロード時に、その幅に応じて style 属性を自動的にかますような。Wikipedia なんかはそんな感じ でしょうし、WordPress のプラグインなんかでもそんなのありそう (調べてない)。

というわけで、やはり CSS だけでなんとかしたいなーというのが以下のコード。前述の Stack Overflow で見つけたやつに少しアレンジを加えたものです:

figure {
    display: table;
    width: 160px; /* minimum width */
    *width: auto; /* for IE7 and below */
}

これだけ。figure はテーブルのように振る舞うので、img の幅が figure に指定した幅を超えた場合はその幅に合わせて広がります。またもし img の幅が figure に指定した幅に満たない場合でもつねにその幅が確保されます。figcaption は横に広がろうとしますが、figure の制限をうけてぴったりの幅に収まります。ちなみにここでは figurefigcaption ですが、divspan やなんかでも同じです。デモを用意したのでどうぞ:

Stack Overflow のバージョンだと width の値が 1px なんですが、それだと img の幅がごく小さいときに figcaption がせまくなり過ぎて読みづらいので、サイトのグリッド構成などに応じて最低限これだけの幅はほしいという値を指定しておくのがいいと思います。ちなみにその場合、img は左に寄せず中央に置くと座りがいいです。

同じく Stack Overflow バージョンは imgfigcaption に対して display: table-row を指定していますが、それは必要ない気がします。検証した限りとくに不都合はないのでたぶん大丈夫だと思うんですが…

また、もし imgmax-width: 100% というスタイルが指定されていると figure の幅に縮められてしまうので注意が必要です。レスポンシヴなデザインでよく使われるスタイルだと思いますが。

display: table をサポートしていない IE7 以下では figure の幅が width の値のとおりに解釈され、結果として縦にひしゃげたかたちになりキャプションが読みづらくなってしまうので、width: auto で上書きしています。とは言えこれで IE8 以上と同じレイアウトになるわけではなく、ただボックスが横に広がるだけです。IE7 では figureimgmax-width を指定すればある程度はコントロールできます (Fig 3) が、もし同じレイアウトでということになると、前述のような JavaScript を用いたり幅を固定したりといった手法が必要でしょう。

Fig 3: IE7 で `figure` 要素と `img` 要素に `max-width` プロパティを指定した場合の表示例

Web コンテンツがスマートフォンやタブレットといった多様なデバイスで閲覧される機会が増え、canvas や SVG といった手法も広まってきたいま、「画像」をいかに取り扱うかというのはあらためて考えなければいけない課題のひとつです。今回ここで紹介したのはレイアウトに関するちょっとしたテクニックに過ぎないですが、将来に向けてできる限りシンプルで汎用性の高い手法を模索していきたい、と考えた結果でもあります。

あけましておめでとうございます!