🦔

【CSS】1行のテキストを両端揃えする方法

2022/11/07に公開

1行のテキストを両端揃えしたいことってありますよね。たとえば以下のようなデザインに出会ったことはないでしょうか。

テキストが両端揃えになっている例の画像

「うどん」「そば」「ラーメン」のテキストが両端揃えになっています。

今回はこのようなデザインをCSS(もしくはHTMLとJavaScript)で実現する方法について考えてみます。

text-align-lastを使う

https://developer.mozilla.org/ja/docs/Web/CSS/text-align-last

CSSのtext-align-lastプロパティが使える環境あれば話は簡単です。

肝となるのは以下の箇所ですね。text-align-last:justifyを設定することで、テキストが両端揃えになりました。

dt {
  text-align-last: justify;
}

上記はベストな解決策に思えます(実際モダンブラウザの最新バージョンを対象にしているならベストだと思います)。しかし、16.0以前のSafariではサポートされていないという懸念点があります。

https://caniuse.com/?search=text-align-last

では、それ以前のブラウザをサポートするためにはどうしたら良いのでしょうか。本記事ではいくつかの実装方法を考えていきます。

スペースを入力する

もっともシンプルな方法はスペースを入れることでしょう。
等幅フォントであれば、スペースを入力することで解決できることがあります(文字数によってはできないこともある)。

ただし、上記を見てもわかるように「うどん」と「そば」では問題ありませんが、「うどん」と「ラーメン」では文字数の関係でバランスが悪くなってしまいます。

letter-spacingを使う

letter-spacingを使って文字間を調節してみましょう。

下記の実装例では等幅フォントで文字間の調節を行なっていますが、等幅ではないフォントを使用している場合は調整が難しいかもしれません(実際に目で見て調節する必要がある)。

letter-spacingは最後の文字の後ろ側にもスペースができてしまうので、マイナスマージンで相殺します。

dt[data-num="3"] {
  /* 相殺マージン */
  margin-right: -0.5em;
  letter-spacing: 0.5em;
}

この方法では文字数によってletter-spacingの値が左右されるので、管理コストが大きくなるかもしれません。また、フォントによっては文字間の調節が非常に難しくなる可能性もあります。

等幅フォントであれば、次のようにCSS変数とcalcを使って管理することが可能です。

dt {
  /*
    --result = (最大の文字数 - 対象の文字数) / (文字間の数)
    今回の最大の文字数は「ラーメン」なので4
  */
  --result: calc((var(--max-num) - var(--num)) / (var(--num) - 1));
  margin-right: calc(var(--result) * -1em);
  letter-spacing: calc(var(--result) * 1em);
}

1文字ずつspanタグで囲む

スペースやletter-spacingを使う方法は、管理コストの問題や使用できる場面が限定的で汎用性に欠ける問題がありました。したがって、実際にはspanで囲う方法を採用することが多いかもしれません。

上記の実装例ではJavaScriptを使用してテキストをspanで囲っていますが、HTML側でspanを書いてもとりわけ問題はありません。

text-alignを使う

ここまでにいくつかの実装を書いてきましたが、本記事を書こうと思ったきっかけはこの方法になります。

https://developer.mozilla.org/ja/docs/Web/CSS/text-align

justify
インラインコンテンツは両端揃えされます。テキストは最終行を除いて、その左右の端が行ボックスの左右の端に揃うように間隔が空けられます。

MDNに記載がある通りtext-align:justifyは最後の行に適用されません(つまり1行だと適用できない)。そもそもtext-align:justifyが1行のテキストに適用できるならtext-align-lastは必要ないじゃん、というのはごもっともな意見です。

ではどうするのかというと、1行ではなく強引に2行にすることでtext-align:justifyを適用します。

重要なのは下記の辺りです。
擬似要素をつかって、dtを2行にしています。加えてgrid-auto-rowsで高さを指定しています。この時、line-heightの計算を忘れないようにしてください。

:root {
  --line-height: 2;
}
dl {
  display: grid;
  grid-template-columns: auto 1fr;
  /* 高さを指定。line-heightの考慮を忘れずに */
  grid-auto-rows: calc(1em * var(--line-height));
  line-height: var(--line-height);
}
dt {
  text-align: justify;
}
/* 2行にするために擬似要素(::after)でinline-blockかつ横幅100%を指定 */
dt::after {
  content: "";
  display: inline-block;
  width: 100%;
}

強引な方法ではありますが、1行のテキストにもtext-align:justifyを適用することができました。

ddが複数行の時

上記ではddのテキストが複数行になったときのことを考慮していませんでした。ddが複数行でも対応できるコードを載せておきます。

grid-auto-rowsで高さを指定しまうと高さが固定されてしまい複数行に対応できなくなるので、grid-auto-rowsを削除しました。代わりにdtの中にspanを入れて、そのspanに対して高さを指定しています。

具体的に変更を加えたのは下記の辺りになります。

<dt><span>うどん</span></dt>
dt > span {
  display: inline-block;
  width: 100%;
  height: calc(1em * var(--line-height));
  vertical-align: bottom;
  overflow: hidden;
}

以上で隣りのddの高さを可変にしつつ、dtは両端揃えにするということが実現できました。

おわりに

text-align-lastは非常に便利ですね。Safari以外ではそれなりのサポート率なので、text-align-lastを使用しつつ、Safari 16.0以前に関しては左寄せ(もしくは右寄せ)で表示するというのも1つの手だと思いました。
また本記事で紹介した方法は、自分で書いておきながら必ずしも良い方法とは言えなかったりもしますが、あくまで1つの手段として頭の片隅に入れておくと、どこかで使い道があるかもしれませんね。

他にも方法がありましたら、是非コメント欄より教えてください!

Discussion