目次
概要
これまで長い文字列がコンテナをはみ出す問題を解決するためにword-break: break-wordを適用していました。しかし最近、この値が非推奨(deprecated)であることがわかりました。MDN公式ドキュメントによると、word-break: break-wordはoverflow-wrap: anywhereとword-break: normalを組み合わせたものと同じ効果を持つレガシー値であり、deprecatedと明記されています。
そこでoverflow-wrap: break-wordに変更したところ、特定の要素で折り返しが動作しない現象が発生しました。
原因を調査した結果、overflow-wrap: break-wordとoverflow-wrap: anywhereの微妙だが重要な違いを発見しました。この記事では、その原因分析と解決方法、そしてStylelintカスタムプラグインによる検査と修正方法の推奨の自動化について共有します。
Stylelintの設定については、以下の記事を参考にしてください。
overflow-wrapとは?
overflow-wrapはコンテナをはみ出す長い文字列を強制的に折り返す安全装置です。通常は何もせず、はみ出す時だけ作動します。
英語の場合
一般的な英語の文章は、以下のようにoverflow-wrapに関係なくスペースで自然に折り返されます。
This is a sample
text for testing
word wrap behavior.
しかし、URLやシリアルナンバーのようにスペースなしで続く長い文字列ははみ出しが発生します。この時overflow-wrapが設定されている場合、はみ出す地点で強制的に折り返します。
- overflow-wrapなし
https://example.com/very/long/path/to/resource ← はみ出し!
- overflow-wrapあり
https://example.co
m/very/long/path/t
o/resource
break-wordとanywhereの違い
overflow-wrapの2つの値であるbreak-wordとanywhereは、見た目の折り返し結果は同一です。
// break-word
Password:
aB3$kL9mNpQrStUvWx
Yz1234567890abcdef
// anywhere
Password:
aB3$kL9mNpQrStUvWx
Yz1234567890abcdef
違いは**レイアウト計算(min-content)**で発生します。
break-word: min-content計算時に折り返し機会を考慮しません。つまり、単語全体の幅が最小幅になります。anywhere: min-content計算時に折り返し機会を考慮します。つまり、1文字幅まで縮小できます。
MDN公式ドキュメントでもこの違いを明確に説明しています:
anywhere: “Soft wrap opportunities introduced by the word break are considered when calculating min-content intrinsic sizes.”break-word: “Soft wrap opportunities introduced by the word break are NOT considered when calculating min-content intrinsic sizes.”
この違いこそが、break-wordが特定の要素で動作しない原因です。
break-wordが動作しない原因
問題の要素にはdisplay: inline-blockが適用されていました。
inline-blockはコンテンツに合わせてサイズが決定されるintrinsic sizing要素です。
この要素の幅はmin-contentを基に決定されます。先ほど説明したmin-content計算の違いがここで問題を引き起こします。
- inline-block + break-wordの場合
min-content = 単語全体の幅 (260px)
→ 要素が260pxに広がる
→ はみ出しが発生しない
→ break-wordがトリガーされない
→ 折り返しなし!
- inline-block + anywhereの場合
min-content = 1文字幅 (~8px)
→ 親に合わせて幅が制限される
→ はみ出し発生
→ 折り返し発生!
まとめると、break-wordは「はみ出した時だけ折り返す」のですが、intrinsic sizing要素のmin-content計算に折り返し機会を含めないため、要素自体が広がってはみ出し自体が発生しなくなります。結果として折り返しがトリガーされないのです。
同じ問題が発生するCSS属性
inline-block以外にもintrinsic sizingを使用するCSS属性はすべて同じ問題が発生します。
| CSS属性 | 理由 |
|---|---|
display: inline-block | コンテンツに合わせて幅決定 |
display: inline-flex | inline-level flexコンテナ |
display: inline-grid | inline-level gridコンテナ |
display: table-cell | コンテンツベースの幅 |
float: left / right | shrink-to-fit |
position: absolute / fixed(width未指定) | shrink-to-fit |
width: min-content / fit-content | 明示的intrinsic sizing |
これらの属性はすべてコンテンツに合わせて幅が決定されるため、break-wordのmin-content計算方式により折り返しが動作しなくなります。
推奨するCSSの組み合わせ
この問題を解決するために、以下の3つのCSSプロパティを合わせて使用することを推奨します。
overflow-wrap: anywhere;
word-break: normal;
line-break: strict;
各プロパティの役割
overflow-wrap: anywhere
長い文字列がコンテナをはみ出さないように強制的に折り返します。break-wordとは異なり、min-content計算に折り返し機会を含めるため、intrinsic sizing要素でも正常に動作します。
word-break: normal
デフォルトの単語分割ルールを使用します。英語はスペースで、日本語・中国語(CJK)は文字単位で折り返します。デフォルト値なので省略可能ですが、以前word-break: break-wordが適用されていた箇所では明示的に追加して意図を明確にすることが望ましいです。
word-breakの各値による動作の違いは以下の通りです:
| 値 | 英語 | 日本語・中国語(CJK) |
|---|---|---|
normal | 単語単位の折り返し | 文字単位の折り返し |
break-all | 文字単位の折り返し | normalと同じ |
keep-all | normalと同じ | 単語単位の折り返し(はみ出し危険) |
line-break: strict
日本語の禁則処理を厳格に適用します。行頭に来てはいけない文字(、 。 ぁ っ 々 ー など)を制御して、より自然なテキストにします。
- strictなし → 「々」が行頭に来る(不自然)
そこは湖のほ
とりで木
々が輝いてい
- strictあり → 「木々」をまとめて維持(自然)
そこは湖の
ほとりで
木々が輝い
英語には実質的な影響がないため、一緒に適用しても副作用はありません。
参考:
3つのプロパティの結合効果
英語テキストの場合:
# 入力: "Visit this link: https://example.com/very/long/path/to/resource"
Visit this link: ← word-break: normal(スペースで折り返し)
https://example.co ← overflow-wrap: anywhere(はみ出し地点で強制折り返し)
m/very/long/path/t
o/resource
日本語テキストの場合:
# 入力: "デバイスID: ABCDEFGHIJKLMNOPQRSTUVWXYZ を確認してください。"
デバイスID: ← word-break: normal(CJK文字単位の折り返し)
ABCDEFGHIJKLMNOPQR ← overflow-wrap: anywhere(英文の強制折り返し)
STUVWXYZを確認し
てください。 ← line-break: strict(「。」が行頭に来ない)
anywhereに統一しても問題ないか?
break-wordとanywhereの視覚的な折り返し結果は同一で、違いはmin-content計算のみです。
anywhereに統一した場合、理論的にはflex/grid/tableレイアウトで空間配分が変わる可能性があります。anywhereはmin-contentが1文字幅まで縮小できるため、flex itemが予想より小さく縮小したり、gridトラックが狭くなる場合があり得ます。
しかし、実務でこれが問題になることはほとんどありません。その理由は以下の通りです:
- min-contentは下限値に過ぎません。 実際のレンダリング幅は
flex-basis、width、grid-template-columnsなどの他のプロパティによって決定されます。min-contentが小さくなったからといって、要素がすぐにそのサイズに縮小するわけではありません。 - flexレイアウトでは
flex-grow/flex-shrinkが空間を配分します。 ほとんどのflex itemはflex: 1や固定のflex-basisを持っているため、min-contentの違いは最終的な幅に影響しません。min-contentが関与するのは、itemがこれ以上縮小できない最小限界を決定する時だけです。 - gridレイアウトでもトラックサイズは明示的に指定されます。
grid-template-columns: 1fr 2frやminmax(200px, 1fr)のような宣言が一般的なので、min-contentが変わってもトラックサイズに影響しません。min-contentキーワードをトラックサイズとして直接使用する場合にのみ違いが生じますが、これはまれなパターンです。 - テーブルでは
table-layout: fixedが一般的です。 固定レイアウトではセル内容ではなく列幅宣言に基づいてサイズが決定されるため、min-contentの違いは無視されます。table-layout: auto(デフォルト)でのみ影響がある可能性がありますが、その場合でもテキストがはみ出す問題より適切に折り返される方がより良い結果になります。
むしろbreak-wordを使用すると、先ほど見たようにintrinsic sizing要素で折り返しが動作しない実質的なバグが発生します。したがって、anywhereに統一するのが安全です。
word-break: break-wordはなぜ非推奨なのか?
word-break: break-wordはCSS3仕様で正式に定義された値ではありません。もともとoverflow-wrap: break-wordのレガシーエイリアス(legacy alias)としてブラウザが後方互換性のためにサポートしてきました。CSS Text Level 3仕様でもこの値を非推奨と明記しており、代わりにoverflow-wrap: anywhereの使用を推奨しています。
Stylelintカスタムプラグインで自動化
このルールを開発者が毎回覚えるのは難しいです。そこでStylelintのカスタムプラグインを作成し、自動で検知してエラーメッセージとともに正しい代替の組み合わせを案内するように設定しました。
Stylelintの設定については、以下の記事を参考にしてください。
検知ルール
| 検知対象 | エラーメッセージ |
|---|---|
overflow-wrap: break-word | 期待通りに折り返されないケースがあるため非推奨 → 3つのセットを案内 |
word-break: break-word | 非推奨(deprecated) → 3つのセットを案内 |
overflow-wrap: anywhere + line-break: strictなし | 「line-break: strictを追加してください」と案内 |
プラグインコード
プロジェクトルートにplugins/require-strict-wrap-rules.jsファイルを作成し、以下のように記述します。
'use strict';
const stylelint = require('stylelint');
const ruleName = 'custom/require-strict-wrap-rules';
const REPLACEMENT =
'代わりに "overflow-wrap: anywhere; word-break: normal; line-break: strict;" を使用してください。';
const messages = stylelint.utils.ruleMessages(ruleName, {
rejectedOverflowWrapBreakWord:
'"overflow-wrap: break-word" は期待通りに折り返されないケースがあるため非推奨です。' +
REPLACEMENT,
rejectedWordBreakBreakWord:
'"word-break: break-word" は非推奨です。' + REPLACEMENT,
rejectedLineBreak:
'overflow-wrap: anywhere を使用する場合は "line-break: strict" も指定してください' +
'(日本語の禁則処理のため)。',
});
const ruleFunction = (primary) => {
return (root, result) => {
const validOptions = stylelint.utils.validateOptions(result, ruleName, {
actual: primary,
possible: [true],
});
if (!validOptions) return;
root.walkRules((rule) => {
let anywhereNode = null;
let hasLineBreakStrict = false;
rule.walkDecls((decl) => {
if (decl.prop === 'overflow-wrap' && decl.value === 'break-word') {
stylelint.utils.report({
message: messages.rejectedOverflowWrapBreakWord,
node: decl,
result,
ruleName,
});
}
if (decl.prop === 'word-break' && decl.value === 'break-word') {
stylelint.utils.report({
message: messages.rejectedWordBreakBreakWord,
node: decl,
result,
ruleName,
});
}
if (decl.prop === 'overflow-wrap' && decl.value === 'anywhere') {
anywhereNode = decl;
}
if (decl.prop === 'line-break' && decl.value === 'strict') {
hasLineBreakStrict = true;
}
});
if (anywhereNode && !hasLineBreakStrict) {
stylelint.utils.report({
message: messages.rejectedLineBreak,
node: anywhereNode,
result,
ruleName,
});
}
});
};
};
ruleFunction.ruleName = ruleName;
ruleFunction.messages = messages;
module.exports = stylelint.createPlugin(ruleName, ruleFunction);
Stylelint設定
プロジェクトルートの.stylelintrc.jsonファイルにプラグインとルールを追加します。
{
"plugins": ["./plugins/require-strict-wrap-rules.js"],
"rules": {
"custom/require-strict-wrap-rules": true
}
}
実行結果
このルールを適用してStylelintを実行すると、以下のようなエラーメッセージが出力されます。
- overflow-wrap: break-word使用時
✖ "overflow-wrap: break-word" は期待通りに折り返されないケースがあるため非推奨です。
代わりに "overflow-wrap: anywhere; word-break: normal; line-break: strict;" を使用してください。
- word-break: break-word使用時
✖ "word-break: break-word" は非推奨です。
代わりに "overflow-wrap: anywhere; word-break: normal; line-break: strict;" を使用してください。
- overflow-wrap: anywhereでline-break: strict欠落時
✖ overflow-wrap: anywhere を使用する場合は "line-break: strict" も指定してください
(日本語の禁則処理のため)。
まとめ
CSSの折り返しプロパティは似ているように見えますが、微妙な違いがあり意図通りに動作しない場合があります。この記事で取り上げた内容をまとめると以下の通りです。
| 非推奨 | 推奨 |
|---|---|
word-break: break-word | overflow-wrap: anywhere |
overflow-wrap: break-word | word-break: normalline-break: strict |
overflow-wrap: anywhereはbreak-wordとは異なり、intrinsic sizing要素(inline-block、inline-flex、floatなど)でも正常に動作します。word-break: normalは英語と日本語(CJK)の両方で最も自然な折り返しを提供します。line-break: strictは日本語の禁則処理を厳格に適用してテキスト品質を高めます。- Stylelintカスタムプラグインでこのルールを自動化すれば、開発者が毎回覚える必要がなくなります。
参考
- MDN - overflow-wrap
- MDN - word-break
- MDN - line-break
- MDN - Intrinsic Size
- MDN - Block Formatting Context
- W3C - CSS Text Level 3: overflow-wrap
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Dekuが開発したアプリを使ってみてください。Dekuが開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。