npm パッケージ
tate-chu-yoko
日本語の縦組み本文で、半角英数字を自動的に「縦中横」として組版する軽量ライブラリ。書き手はプレーンテキストのままで、組版はライブラリに任せましょう。
第1章 2026年4月、Webの縦書きは進化した。
解決したいこと
新聞や書籍の縦組みでは、半角英数字を横並びのまま正立させる「縦中横」が当たり前に使われてきました。CSS には text-combine-upright: all があるものの、どの文字をまとめるかは開発者が明示しなければなりません。結果として、書き手が原稿の中で <span class="tcy"> を毎回書く羽目になりがちです。tate-chu-yoko はこの組版処理を自動化し、書き手を組版の都合から解放します。
3つの配布物
用途に合わせて必要なパッケージを選べます。コアは framework-agnostic なトークナイザー、React / Vue は公式アダプタです。
@love-rox/tcy-core
Framework-agnostic トークナイザー
文字列を走査し、縦中横にすべき箇所を構造化されたトークン列として返します。独自の UI フレームワークやビルド時変換に組み込めます。
@love-rox/tcy-react
React コンポーネント
子要素を再帰的に走査し、縦中横対象の runs を <span class="tcy"> で包む <Tcy> コンポーネントを提供。React 17+ 対応、SSR で決定性のある出力を保証します。
@love-rox/tcy-vue
Vue 3 コンポーネント
Vue 3 向けの <Tcy> コンポーネント。React 版と同じオプション・挙動で、Vue アプリケーションにそのまま組み込めます。
インストール
使いたいアダプタ(React / Vue)だけ入れれば、@love-rox/tcy-core は依存で自動的に引き込まれます。
bun
bun add @love-rox/tcy-reactpnpm
pnpm add @love-rox/tcy-reactnpm
npm i @love-rox/tcy-reactyarn
yarn add @love-rox/tcy-react使い方
writing-mode: vertical-rl を当てた親要素の中で、縦中横対象にしたい範囲を <Tcy> で包むだけです。
import { Tcy } from "@love-rox/tcy-react";
export function Chapter() {
return (
<p style={{ writingMode: "vertical-rl" }}>
<Tcy>第1章 2026年4月、Webの縦書きは進化した。</Tcy>
</p>
);
}<script setup lang="ts">
import { Tcy } from "@love-rox/tcy-vue";
</script>
<template>
<p style="writing-mode: vertical-rl">
<Tcy>第1章 2026年4月、Webの縦書きは進化した。</Tcy>
</p>
</template>import { tokenize } from "@love-rox/tcy-core";
tokenize("第1章 2026年4月");
// [
// { type: "text", value: "第" },
// { type: "tcy", value: "1" },
// { type: "text", value: "章 " },
// { type: "tcy", value: "2026" },
// { type: "text", value: "年" },
// { type: "tcy", value: "4" },
// { type: "text", value: "月" },
// ]推奨CSS
生成される <span class="tcy"> に以下を当ててください。
.tcy {
-webkit-text-combine: horizontal;
text-combine-upright: all;
}オプション
React / Vue の <Tcy> と tokenize() で共通のオプションです。
共通オプション
| オプション | 型 | 既定値 | 説明 |
|---|---|---|---|
| target | 'alphanumeric' | 'alpha' | 'digit' | 'ascii' | RegExp | 'alphanumeric' | 縦中横対象として扱う文字種。alphanumeric は [0-9A-Za-z]、ascii は印字可能な ASCII 全般。任意の RegExp も指定可能。 |
| combine | boolean | true | 連続した対象文字を1つのスパンにまとめるか。false にすると1文字ずつ別々のスパンで包みます。 |
| include | string | string[] | undefined | target に含まれなくても対象として扱う追加の文字。 |
| exclude | string | string[] | undefined | 対象から外したい文字。include より優先されます。 |
コンポーネント専用プロパティ
| オプション | 型 | 既定値 | 説明 |
|---|---|---|---|
| className | string | 'tcy' | 生成される span に付与するクラス名。 |
| as | keyof JSX.IntrinsicElements | 'span' | ラップに使用するタグ名。 |
挙動の注記
<em>12</em>34のように要素境界をまたぐ文字列は連結されず、別々のスパンになります。- 全角英数字(
A〜Z/0〜9)は既定で対象外。縦組みでも既に正立して表示されるためです。 - React / Vue のアダプタはどちらも SSR-safe で、サーバーとクライアントで同じ DOM を生成します。
ブラウザ対応
writing-mode: vertical-rl と text-combine-upright: all は主要なモダンブラウザで動作します。Safari では歴史的に -webkit-text-combine: horizontal を併記しておくと安心です。
ライセンス
MIT License。個人・法人問わず自由に利用できます。
触ってみる
設定を変えながらブラウザ上で挙動を確認できるデモを用意しました。
デモページを開く