全部大文字のテキストに text-transform: capitalize が絶対に効かない理由


比較的ベーシックなプロパティであるtext-transformに知らない仕様があったのと、
ちょうどCSSアドベンドカレンダーに空きがあったので投稿します!

text-transformは、文字の大文字小文字の表示を変更するプロパティです。

一番想定される使い方は、欧文の大文字/小文字の変換です。
使えるキーワードと効果は次のように記憶していました。 1

  • uppercase: 大文字に変換
  • lowercase: 小文字に変換
  • capitalize: 単語ごとに先頭を大文字にして、ほかは小文字にする

「何を今更」と思われるかもしれませんが、上記の理解は誤りでした。下のCodePenを見てください。

See the Pen text-transform: capitalize does not effect if the text is all-caps by y_hokkey (@hokkey) on CodePen.

このように、text-transform: capitalizeは、全部大文字のテキストには効果がありません。

これは仕様通りの挙動です。

CSS 1 仕様書

'capitalize'
uppercases the first character of each word
Cascading Style Sheets, level 1

意訳: それぞれの単語の最初の文字を大文字にする。

CSS 2.1 仕様書

capitalize
Puts the first character of each word in uppercase; other characters are unaffected.
Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification

意訳: それぞれの単語の最初の文字を大文字にする。それ以外の文字には影響しない。

CSS Text Module Level 3 仕様書(暫定)

capitalize
Puts the first typographic letter unit of each word, if lowercase, in titlecase; other characters are unaffected.
CSS Text Module Level 3

意訳: それぞれの単語の最初のtypographic letter unitが小文字の場合にそれを大文字にする。それ以外の文字には影響しない。

細かいことは違いますが、一貫して単語の1文字目にしか言及していないことが分かります。
つまり、全部大文字の単語の2文字目以降を小文字にする機能はtext-transform: capitalizeにはありません。

AngularのPipeとは挙動が違う

話が急にAngularへ飛びます。

Angularの標準のPipeである uppercase, lowercase, titlecaseは、表示でなく出力する文字自体を置き換える機能ですが、その変換の挙動はtext-transformと似ています。

しかし、こちらの機能は2文字目以降にも影響してくれるため、

<p>{{ 'HELLO WORLD' | titlecase }}</p>

の出力は

<p>Hello World</p>

と同じになります。最近Angularをよく使っていたため、このtitlecaseの挙動がtext-transform: capitalizeと一緒だと誤解してしまっていた。

CSSだけで常にtitlecaseにする方法はまだない

.alternative-capitalize {
  text-transform: lowercase;

  &:first-letter {
    text-transform: uppercase;
  }
}

なお上記のようなコードで、1文字目だけを常に大文字化することは可能ですが、単語ごとに1文字目を大文字化することはやっぱり実装できません。

おわりに

何年CSS書いてるんだっていう感じですが、非常にベーシックなプロパティすら完全には把握していなかったのが残念でした。これからも謙虚な気持ちで学んでいきたいものです!


  1. full-widthの挙動は記事のテーマと関係しないので話のスコープから外します。