こんにちは。小学生向けのニュースサイト、こどもニュースをつくっています。
このサイトの記事には、表示設定のボタンが並んでいます。ふりがなの有無、文字の大きさ、縦書きか横書きかです。
ボタンのラベルには、選択中の文字を通常サイズで、他の選択肢を小さく灰色で表示しています。「かなあり / なし」のような形です。
この 2 つの文字が、微妙に揃っていませんでした。
何がずれるか
flex で横に並べるとき、既定でよく使うのは中央揃えです。
display: inline-flex;
align-items: center;
これは要素の箱を中央で揃えます。文字を揃えているわけではありません。
フォントサイズが違うと、行の高さも違います。中央で揃えると、小さいほうの文字が箱の中央に来るので、大きい文字の中心と一致します。
ところが、人間が「揃っている」と感じるのは、文字の下端(ベースライン)が一致しているときです。中央で揃えると、小さい文字が少し浮いて見えます。
差は 1px か 2px 程度です。それでも、並んでいると気になります。
baseline で揃える
対処は、その部分だけ揃え方を変えることです。
{/* サイズ違いのラベルを中央 (align-items: center) で並べると小さい文字が
ベースライン差ぶん浮いて見えるため、文字はベースラインで揃える。 */}
<Box component="span" sx={{ display: 'inline-flex', alignItems: 'baseline', gap: 0.75 }}>
{children}
</Box>
align-items: baseline は、文字のベースラインを揃えます。フォントサイズが違っても、下端が一致します。
ボタン全体の配置は中央のままで、文字を並べる内側の要素だけベースラインにしています。ボタンの中でラベルを中央に置きたいのと、ラベル同士を揃えたいのは別の話なので、階層を分けました。
アイコンだけは例外
このボタンには、文字ではなくアイコン(SVG)のラベルもあります。縦書きと横書きの切り替えがそれです。
SVG にはベースラインがありません。baseline で揃えると、要素の下端が文字のベースラインに合わせられて、沈んで見えます。
なので、アイコンのときだけ中央揃えに戻します。
sx={{
display: 'inline-flex',
alignItems: 'center',
// アイコンラベル (SVG) はテキストベースラインを持たないため中央揃えに戻す
...(typeof option.label === 'string' ? {} : { alignSelf: 'center' }),
…
}}
ラベルが文字列かどうかで判定しています。型で分岐できるので、追加の指定は要りません。
align-self を使っているのが要点です。親の align-items を変えずに、その要素だけ揃え方を変えられます。
揃え方を選ぶ基準
整理すると、こうなります。
- 文字同士を並べる →
baseline - 文字とアイコンを並べる →
center - 箱同士を並べる →
center
「何を揃えたいのか」で決まります。文字の見た目を揃えたいなら、箱ではなく文字の基準線を使います。
flex の align-items は既定で stretch で、実務では center を書くことが多いです。ほとんどの場面ではそれで問題ありませんが、サイズの違う文字を並べるときだけは baseline を思い出す価値があります。
気づき方
この種のずれは、スクリーンショットを撮って拡大すると分かります。目視だと「なんとなく揃っていない」で終わってしまい、原因の特定に至りません。
拡大して、文字の下端に横線を引いてみると、ずれている量が見えます。そこまで確かめると、baseline にすればよいと分かります。
「なんとなく気持ち悪い」を放置しないためには、気持ち悪さを数値にする手段が要ります。1px か 2px かが分かれば、対処は自然に決まりました。
まとめ
-
align-items: centerは箱を揃える。文字を揃えているわけではない - フォントサイズが違う文字を中央で揃えると、小さいほうが浮いて見える
- 文字同士を並べるなら
align-items: baselineを使う - SVG にはベースラインが無いので、そこだけ
align-self: centerで戻す - ボタンの中の配置と、ラベル同士の揃え方は階層を分けて指定する
- 「なんとなく揃っていない」は拡大して数値で確かめる