CSSで、文章を縦書きにする方法
CSS で文章を縦書きにするには、文章を囲む要素に writing-mode: vertical-rl を指定します。文字は上から下へ並び、行は右から左へ進みます。あわせて height で1行の長さを決めておくと、行の長さが画面の高さに左右されません。
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。フォントは Noto Serif CJK JP を使いました。HTML と CSS のページを Playwright で開き、要素の位置と大きさ、getComputedStyle() の値、スクロールの位置を調べました。Firefox と Safari、スマートフォンの実機、ほかのフォントでの見え方は試していません。
writing-mode: vertical-rl で縦書きにする
CSS は次のとおりです。
/* 文章を縦書きにする */
.tategaki {
writing-mode: vertical-rl;
height: 16em;
}
<div class="tategaki">
<p>吾輩は猫の手である。名前はまだない。</p>
<p>どこで生まれたか、とんと見当がつかぬ。</p>
</div>
writing-mode は、横書きにするか縦書きにするかと、行が進む向きを決めるプロパティです。vertical-rl は、文字を上から下へ並べ、次の行を前の行の左に置きます。
検証では、文字の大きさを20px、行の高さを1.8にしました。縦書きの箱は高さが320px(16em)で、幅は144pxでした。1つの段落が2行に折り返し、1行が36px、2行で72pxの幅になります。1つ目の段落は箱の右半分(段落の左端が、箱の左端から72pxの位置)に、2つ目の段落はその左(箱の左端の位置)に置かれました。
縦書きでは、height が1行の長さになり、行が増えると幅が広がります。getComputedStyle() で論理的な大きさを調べると、inline-size(行の長さ)が 320px、block-size(行が積み重なる方向の長さ)が 144px でした。横書きのときとは、幅と高さの役目が入れ替わります。
句読点の「、」「。」は、縦書きのときの位置(文字の右上)に置かれました。次の節の画像で確かめられます。
vertical-lr との違いは、行が進む向き
writing-mode には vertical-lr もあります。文字が上から下へ並ぶのは同じですが、次の行は前の行の右に置かれます。

位置を測ると、vertical-lr では1つ目の段落の左端が箱の左端に、2つ目の段落の左端が箱の左端から72pxの位置にあり、vertical-rl とちょうど逆でした。
height を指定しないと、行の長さが画面の高さで変わる
height を指定せずに writing-mode: vertical-rl だけを書くと、1行の長さはブラウザの画面の高さで決まりました。同じ文章(73文字)を、画面の高さだけを変えて開いた結果です。
| 画面の高さ | 縦書きの箱の高さ | 行数(箱の幅) |
|---|---|---|
| 400px | 400px | 4行(144px) |
| 800px | 800px | 2行(72px) |
箱の高さが画面の高さと同じになるため、body の上下の余白(20pxずつ)を足したページ全体の高さは、画面より40px高くなりました。画面の高さが変わると行の折り返しも変わるので、縦書きにするときは height で行の長さを決めておくと、表示が安定します。
英字を立てて並べる text-orientation: upright
縦書きの中の英字は、初期設定では右に90度倒れて並びます。1文字ずつ立てて並べたいときは、text-orientation に upright を指定します。
/* 英字を横に倒さず、1文字ずつ立てて並べる */
.upright {
text-orientation: upright;
}
初期値の mixed は、英字のような横書きの文字を右に90度回し、漢字やかなはそのまま立てて置きます。検証では、「CSS」の3文字が占める縦の長さは、mixed で36.3px、upright で60pxでした。upright では1文字ごとに20px(1em)ずつ場所を使うので、英字の並びが長くなります。
2桁の数字を横に並べる text-combine-upright: all
日付の「11月」のような2桁の数字は、縦書きの中で横に並べて1文字分に収めることがあります(縦中横)。CSS では、数字を span で囲み、text-combine-upright に all を指定します。
/* 縦書きの中の2桁の数字を、横に並べて1文字分に収める */
.tcy {
text-combine-upright: all;
}
<p>今日は<span class="tcy">11</span>月。</p>

all は、囲んだ文字をすべて横に並べ、縦書きの行の中で1文字分の場所に収めます。検証では、「11」が占める縦の長さは20px(1文字分)でした。指定しないときは、横に倒れて18.8pxです。
4桁の「2026」に all を指定しても、縦の長さは同じく20pxでした。4文字が1文字分の幅に収まるように縮められます。指定しないときの「2026」は、横に倒れて44.6pxでした。
行がはみ出すと、横スクロールは右端から始まる
縦書きの箱に width と overflow-x: auto を指定すると、行が入りきらないときに横スクロールが出ます。
.tategaki-scroll {
writing-mode: vertical-rl;
height: 16em;
width: 300px;
overflow-x: auto;
}
15行(540px)の文章を幅300pxの箱に入れたところ、開いた直後は右端が見えていて、1行目から8行目までが表示されました。読み始めの位置が最初から見えているので、スクロールの位置を動かす必要はありません。
一方、JavaScript で読むスクロールの位置(scrollLeft)は、開いた直後が 0 で、左(文章の終わり)へ進むほど負の数になりました。いちばん左までスクロールしたときは -240 です。スクロールの位置を JavaScript で扱うときは、0から負の数の範囲になることに気をつけてください。
対応しているブラウザ
MDN の互換性データ(@mdn/browser-compat-data 8.1.8)で、この記事で使っている機能を調べました。最新の対応状況は、MDN の各プロパティのページ(例:MDN の writing-mode のページ)の下にある表で確かめられます。
| 機能 | Chrome | Firefox | Safari |
|---|---|---|---|
writing-mode |
48 | 41 | 10.1 |
text-orientation |
48 | 41 | 14 |
text-combine-upright: all |
48 | 48 | 15.4 |
互換性データでは、Safari は 5.1 から、-webkit- を付けた -webkit-writing-mode と -webkit-text-orientation にも対応しています。





