長い URL とパスの折り返し
長い URL・ファイルパス・パッケージ識別子を、本文(prose)やコードを壊さずに区切り文字を意識した位置で折り返す方法。
問題
サイドバー、テーブル、チャットバブル、コールアウトボックスなどの幅の狭いコンテナには、ブラウザにとって折り返し位置が判断できないような長い一塊のトークンが日常的に流し込まれます。
@scope/org-name/packages/feature-module/src/components/widget.tsx
https://example.com/docs/guide/section?utm_source=newsletter&utm_medium=email&utm_campaign=launch
C:\Users\Example\AppData\Local\Temp\build-artifact-2026-04-24.log
ブラウザはデフォルトでこれらをすべて1つの単語として扱います。トークンはコンテナの端を突き破り、レイアウト全体に横スクロールバーを発生させたり、隣接する列にはみ出したりします。AIエージェントは word-break: break-all や overflow-wrap: anywhere に飛びついて修正を投入し、1週間後にバグ報告を受け取ります。段落という段落が単語の途中でランダムに折り返されるようになり、本文が読めなくなった、というものです。
本当の問題は、ブラウザがどのトークンがURLに似ていて、どのトークンが本文なのかを知らないということです。一律のルールは幅の狭いコンテナの問題を解決する代わりに、ページ上の他のすべてを犠牲にします。
ワンライナーでは不十分な理由
これらのプロパティにはそれぞれ用途がありますが、汎用的な解決策になるものはありません。
| プロパティ | 効果 | なぜ正解にならないか |
|---|---|---|
word-break: break-all | 任意の2文字間、どこででも折り返す | 本文を単語の途中で粉々にし、区切り文字を無視し、ページの残りの部分を読めなくする |
overflow-wrap: anywhere | 同じ効果だが、単語があふれる場合にのみ発動する | 依然として区切り文字を無視する。長い単語は / や ? ではなく任意の文字位置で折り返される |
hyphens: auto | 辞書由来の位置にソフトハイフンを挿入する | URLやパスには何もしない — 辞書の単語ではないため |
word-break: break-word | Chromeレガシーの非標準値。より積極的に折り返し、おおむね word-break: normal と overflow-wrap: anywhere の組み合わせに相当する | overflow-wrap: break-word の同義ではない。緊急時の折り返しの積極度は anywhere と同等で、同じく区切り文字を無視する。使わず、上記の標準プロパティを選ぶこと |
https: のようなURLに対する正しい答えは、ほぼ常に「/ の後で折り返す」です。C: のようなパスに対する正しい答えは「\ の後で折り返す」です。上記のプロパティのどれもこのことを知りません。
<wbr> 注入戦略
<wbr> は word break opportunity 要素です。ブラウザに「ここで折り返す必要があるなら、ここは折り返していい場所だ」と伝えます。折り返しが必要ない場合、視覚的な出力はゼロです。
<wbr> の検証済みの性質:
- グリフはレンダリングされない — 視覚的なスペースを取らない
- スクリーンリーダーには読み上げられない — アクセシビリティのノイズにならない
- クリップボードにコピーされない — ユーザーがURLを選択すると元の文字列がそのまま得られる
- 選択中のキャレットは正常に動作する — 1文字ずれるようなことはない
- SSR-safe — 単なるHTMLで、クライアントサイドのJavaScriptは不要
戦略はこうです。URLに似たトークンの中の区切り文字を見つけて、各区切り文字の 後 に <wbr> を注入します。レンダリングされた文字列はコピー時にバイト一致します。トークンは任意の文字位置ではなく、意味のある境界で折り返されます。
前:
<code>@scope/org-name/packages/feature-module/src/widget.tsx</code>
後:
<code>@scope/<wbr>org-name/<wbr>packages/<wbr>feature-module/<wbr>src/<wbr>widget.tsx</code>
plain セルは水平方向にあふれます。break-all は任意の文字位置で折り返すため、URLは読めません。anywhere も似たように任意の位置で折り返しますが、オーバーフローが起こる場合のみ発動します。smart-break は /、?、& の後できれいに折り返します。
区切り文字の集合
/ は第一の区切り文字です — URL、UNIXパス、パッケージ識別子をカバーします。検討に値する完全な集合は以下のとおりです。
| 区切り文字 | なぜこの後に注入するか |
|---|---|
/ | 第一選択。URLのパスセグメント、UNIXパス、スコープ付きパッケージ名 |
\ | Windowsパス (C:) |
. | ドット区切り識別子 (com.example.app)、ファイル名、ホスト名 |
- | ケバブケース識別子、長いスラッグ |
_ | スネークケース識別子 |
: | ポート区切り (host:8080)、プロトコル (https:)、名前空間 (ns:value) |
? | クエリ文字列の境界 |
# | フラグメントの境界 |
& | クエリパラメータの区切り |
= | クエリのキー/値の境界 |
<wbr> は区切り文字の 後 に注入し、前ではありません。区切り文字は前の行に残り、次のセグメントは次の行の先頭から始まります — 自然な読み順です。
isPathLike ゲート(本文ガード)
ドキュメント中のすべての /、.、- の後に <wbr> を注入すると、通常の本文が破壊されます。
and/orがand/<wbr>orになるwell-knownがwell-<wbr>knownになるstate-of-the-artがstate-<wbr>of-<wbr>the-<wbr>artになる1.2.3-beta.4は折り返し候補の紙吹雪になるUI/UXがUI/<wbr>UXになる
これらのトークンは1行に収まるのが普通なので、通常はレンダリングに変化は出ません — しかしそのトークンは以後、幅の狭いあらゆるコンテキストで折り返し候補になります。
注入は パス風(path-like) チェックで制御します。トークンがパス風なのは、次のいずれかに見える場合です。
/を2つ以上含む (/、a/ b/ c packages/)widget/ src - URLスキームで始まる (
https:、/ / file:、/ / ftp:)/ / - Windowsのドライブレターで始まる (
C:、\ D:)\ Users\ . . . ?の後に=を含む(クエリ文字列の形)
そのままにすべき例:
| トークン | パス風? |
|---|---|
and/or | No — / が1つだけ、スキームなし |
well-known | No — - が1つだけ、スラッシュなし |
state-of-the-art | No — ダッシュのみ |
1.2.3-beta.4 | No — バージョン文字列であってパスではない |
UI/UX | No — / が1つで、周りがすべて大文字 |
/ | Yes — / が3つ以上 |
https: | Yes — スキームあり |
C: | Yes — Windowsドライブ |
?q=hello&lang=ja | Yes — クエリ形 |
トークンをパス風だと自信を持って分類できない場合は、注入しないでください。偽陰性(1つのトークンが狭いコンテナで折り返されないまま残る)のコストは、偽陽性(本文がどこでも不自然な位置で折り返される)のコストより大幅に低いです。
コンテナ側の CSS 調整
<wbr> は折り返し位置の候補を提示するだけです。そこで実際にどうするかはコンテナのCSSが決めます。面によって望ましいコンテナルールは異なります。
| コンテキスト | 推奨ルール | 理由 |
|---|---|---|
| 本文(段落、リスト項目、カード) | overflow-wrap: break-word | トークンがあふれる場合のみ <wbr> で折り返す。通常の単語はそのまま |
コードブロック (<pre><code>) | white-space: pre-wrap — 水平スクロールを受け入れるか、ユーザー切り替えモードとして overflow-wrap: anywhere を追加 | pre-wrap はインデントと改行を保持する。pre-wrap の中では、1つの長いトークンに対して <wbr> だけでは足りない |
Diffビューア(+/- カラム、狭いガター) | word-break: break-all | セルが隣接要素にあふれないほうが重要で、区切り文字を意識した折り返しよりも収めることが優先される |
| 狭いflexパネル(サイドバー、チャットバブル) | overflow-wrap: break-word かつ flex子要素に min-width: 0 | min-width: 0 がないと、flexアイテムの min-content サイズは最長の単語に等しくなり、縮まなくなる |
警告: 同じ要素で word-break: break-all と smart-break を混在させない
word-break: break-all はブラウザに「任意の2文字の間で折り返してよい、以上」と伝えます。このルールが有効になると、ブラウザは行に収まる最初の文字位置で折り返し、<wbr> の折り返し候補ヒントを参照しなくなります。区切り文字を意識した注入は完全に無駄になります。要素ごとに戦略を1つだけ選んでください。区切り文字を意識した折り返しならsmart-break(<wbr> + overflow-wrap: break-word)を、diffセルのような「何としても収める」面なら word-break: break-all を使います。両方を併用してはいけません。
警告: フェンスコードに white-space: pre-wrap を使うと overflow-wrap: break-word が効かなくなる
white-space: pre-wrap はリテラルな空白と改行を保持します。コードブロックに望ましい挙動です。副作用として、長い一塊のトークン(ログ行の中の180文字のURLなど)が overflow-wrap: break-word に応答しなくなります — このルールは必要なときにのみ折り返す仕様であり、pre-wrap のもとではトークンは自前のオーバーフロースクロールする行に技術的には「収まって」しまうからです。コードブロックには2つの選択肢があります。
- 水平スクロールを受け入れる。コードはバイト正確なまま、ユーザーがスクロールします。
- 明示的なオプトインモードとして
overflow-wrap: anywhereを提供する(break-wordより強く、必要ならトークンの途中でも折り返す)。これはユーザー切り替え可能なクラスに限定し、デフォルトにしないでください。
コードブロックに word-break: break-all を持ち出してはいけません — 1行ごとに識別子の途中でコードが粉々になります。
break-word は、単一セグメント自体があふれるまではセグメントを壊しません — そのため区切り文字付きのURLは /、?、& できれいに折り返されます。anywhere はもっと積極的です。min-content 幅に寄与するので、flex親要素が最長セグメントより狭くなることが許されます。anywhere を使うのは、break-word では不十分(1つのセグメントがコンテナより長い)で、識別子の途中で折り返すことを受け入れられるときだけにしてください。
ゼロ幅スペース (U+200B) — テキスト専用のフォールバック
U+200B(ゼロ幅スペース、​ とも書く)は文字列の中に折り返し候補を挿入する文字です。これは <wbr> と同等の推奨手段 ではありません。HTMLが使えない場面 — 長い文字列が要素を含められないプレーンテキスト面に存在しなければならないとき — にのみ使います。
HTMLを出力できない面:
title属性の値(ブラウザのツールチップテキスト)aria-labelの値- HTMLを解釈しないコンポーネントに渡されるプレーンなJSON文字列
- 表示時にそれでも折り返さなければならないCSVやテキストファイルのエクスポート
<wbr> と比較したトレードオフ:
- クリップボードの汚染。
U+200Bは実在する文字です。選択と一緒にコピーされ、貼り付け先にそのまま貼り付けられます。ゼロ幅スペースを含むURLを端末やURLフィールドに貼り付けると、検出しづらい失敗が起こることがあります。 - 文字列長の歪み。
"hello".lengthと"hello".length— 後者は6になります。バリデータ、diffツール、バイト数UIは、ユーザーが見ているものとは異なる値を報告します。 - アクセシビリティ保証が弱い。 スクリーンリーダーはたいてい無視しますが、リーダーやバージョンによって挙動が異なります。
<wbr>には「読み上げない」という明文化された契約があります。 - キャレットの挙動。
U+200Bをナビゲート可能な文字として扱うエディタもあります。矢印キーでの移動中にキャレットがそこで止まることがあります。
経験則としては、HTMLを出力できるなら <wbr> を使ってください。U+200B はテキスト専用面にのみ使います。次のメンテナが単純な文字列に戻して折り返し能力を失わないように、コードコメントに判断の理由を記録しておきましょう。
左のパネルはあふれるか、きれいに折り返されません。右のパネルは挿入された U+200B の位置で折り返されます — <wbr> と同じ見た目ですが、コピー&ペーストに不可視文字が持ち運ばれます。
HTMLが使える場面では常に <wbr> を使ってください。
AIがよくやるミス
word-break: break-allを一律ルールとして持ち出す。目の前の狭いコンテナは1つ修正できますが、サイト全体の段落やコードブロックをそっと台無しにします。- flex子要素に
min-width: 0を付け忘れる。flexアイテムのデフォルトのmin-width: autoはmin-contentに等しく、これは最も長い単語です。flexカラム内の長いURLは縮まなくなり、行全体をオーバーフローさせます。子要素にmin-width: 0を付ければ解放されます。 isPathLikeゲートなしに<wbr>をグローバルに注入する。and/or、state-of-the-art、1.2.3-beta.4のような本文中のトークンが、本来なるべきでない折り返し候補になります。U+200Bと<wbr>を同じものとして扱う。<wbr>はHTMLのみ、U+200Bは文字列中の文字です。コピー&ペーストや文字列長への影響は異なります。- 同じ要素でsmart-breakと
word-break: break-allを混ぜる。break-allは<wbr>のヒントを無視するので、区切り文字を意識した注入は無駄な作業になります。 <pre>や<code>ブロックにoverflow-wrap: break-wordを当てて、長いトークンが折り返されることを期待する。これらの要素にはすでにwhite-space: pre-wrapが効いていて、単一の長いトークンに対してはbreak-wordを無効化します。スクロールを受け入れるか、明示的にoverflow-wrap: anywhereにオプトインしてください。
使い分け
smart-break(<wbr> + overflow-wrap: break-word)を使うべき場面
パス風のトークンを含む本文面: サイドバー、カード、チャットバブル、長い識別子カラムを持つテーブル、エラーメッセージパネル、コールアウト。注入は isPathLike で制御します。ユーザー生成やシステム生成のURLやファイルパスをレンダリングするあらゆるUIにとって、これがデフォルトです。
U+200B を使うべき場面
HTMLを出力できないテキスト専用の面: title 属性、aria-label の値、HTMLを解釈しないコンポーネントに渡されるプレーン文字列のJSONフィールド。HTMLが使える場所ではどこでも <wbr> を優先します。将来のメンテナが意図を理解できるよう、呼び出し箇所で選択理由を記録してください。
スクロールや pre-wrap のままにしておくべき場面(コードとdiff)
バイト正確さが重要なコードブロックでは、人為的な折り返し候補より水平スクロールを優先してください。「長い行を折り返す」オプトインのトグルを用意し、overflow-wrap: anywhere に切り替えるようにします。狭いガターを持つdiffビューアでは、区切り文字を意識した折り返しより列に収めることが重要なので、セル内容に word-break: break-all が必要になる場合があります。
参考
- MDN: <code><wbr></code>
- MDN: <code>overflow-wrap</code>
- MDN: <code>word-break</code>
- MDN: <code>hyphens</code>
- Wikipedia: Zero-width space (U+200B)
isPathLike ゲートを備えた <wbr> 注入戦略の参考実装は zudo-doc に記録されています(エピック zudolab/zudo-doc#370、PR zudolab/zudo-doc#383)。