📝
Next.jsで日本語文章の段落字下げを実装する際の注意点と解決法
はじめに
日本語の読み物アプリを開発している際、段落の1字下げが一部で機能しないという問題に遭遇しました。具体的には、管理画面で追加したコンテンツの最初の段落だけが、手動でスペースを入れても字下げされないという現象でした。
この記事では、その原因の調査から解決まで、実際のコード例と共に解説します。
問題の詳細
症状
- 管理画面で文章を追加する際、最初の段落が字下げされない
- 2段落目以降は正常に字下げされる
- 手動でスペースを入れても効果がない
想定していた動作
- 全ての段落で自動的に1字下げが適用される
- 既に手動でスペースが入っている場合は、2重字下げにならない
原因の調査
1. 文章表示コンポーネントの確認
まず、文章を表示しているTextWithImages
コンポーネントを確認しました:
// src/components/TextWithImages.js
export default function TextWithImages({ text, images = [], className = "" }) {
// ...
return (
<div className={`prose max-w-none ruby-container ${className}`}>
<div className="text-lg text-gray-800" style={{ lineHeight: '2.2' }}>
{renderTextWithImages}
</div>
</div>
);
}
2. CSSスタイルの確認
globals.css
を確認したところ、段落の字下げに関するスタイルが定義されていないことが判明しました。これが根本的な原因でした。
/* globals.css - 字下げスタイルが存在しない状態 */
.ruby-container {
line-height: 2.2;
}
/* 段落字下げのスタイルが不足 */
解決策の実装
1. CSSに段落字下げスタイルを追加
/* globals.css */
/* 日本語段落の字下げスタイル */
.japanese-text {
text-indent: 1em;
}
.japanese-text p {
text-indent: 1em;
margin-bottom: 1em;
}
/* 段落の字下げ(改行で区切られた段落の処理) */
.paragraph-indent {
text-indent: 1em;
}
/* 手動でスペースがある段落は自動字下げを無効化 */
.paragraph-manual-indent {
text-indent: 0;
}
2. 手動スペース検出機能の実装
既存コンテンツで手動スペースを使用している場合の2重字下げを防ぐため、スペース検出機能を追加しました:
// src/components/TextWithImages.js
const renderRubyText = React.useCallback((textContent) => {
const parts = parseRubyText(textContent);
return parts.map((part, index) => {
if (part.type === 'ruby') {
return (
<ruby key={index} className="ruby-text">
{part.content}
<rt className="ruby-annotation">{part.ruby}</rt>
</ruby>
);
} else {
// 段落に分けて処理
const paragraphs = part.content.split(/\n\s*\n/);
if (paragraphs.length > 1) {
return (
<span key={index}>
{paragraphs.map((paragraph, pIndex) => {
// 手動スペースがあるかチェック(行頭に全角・半角スペースまたはタブ)
const hasManualIndent = /^[\s \t]/.test(paragraph);
const indentClass = hasManualIndent ? 'paragraph-manual-indent' : 'paragraph-indent';
return (
<span key={pIndex}>
{pIndex > 0 && <><br/><br/></>}
<span className={indentClass}>
{paragraph.trim()}
</span>
</span>
);
})}
</span>
);
} else {
// 単一段落の場合も手動スペースチェック
const hasManualIndent = /^[\s \t]/.test(part.content);
const indentClass = hasManualIndent ? 'paragraph-manual-indent' : 'paragraph-indent';
return (
<span key={index} className={`whitespace-pre-line ${indentClass}`}>
{part.content}
</span>
);
}
}
});
}, []);
3. メインコンテナへのクラス追加
return (
<div className={`prose max-w-none ruby-container japanese-text ${className}`}>
<div className="text-lg text-gray-800" style={{ lineHeight: '2.2' }}>
{renderTextWithImages}
</div>
</div>
);
実装のポイント
1. 正規表現による手動スペース検出
const hasManualIndent = /^[\s \t]/.test(paragraph);
この正規表現で以下をチェックしています:
-
\s
: 半角スペース -
-
\t
: タブ文字
2. 条件付きクラス適用
const indentClass = hasManualIndent ? 'paragraph-manual-indent' : 'paragraph-indent';
手動スペースの有無に応じて異なるCSSクラスを適用することで、適切な字下げを実現しています。
3. プレビュー機能との整合性
管理画面のプレビュー機能も同じTextWithImages
コンポーネントを使用しているため、実際の表示と完全に一致したプレビューが可能です:
export function TextWithImagesPreview({ text, images = [], className = "" }) {
return (
<div className={`border border-gray-300 rounded-lg p-4 bg-gray-50 ${className}`}>
<div className="text-sm text-gray-600 mb-2 font-semibold">📖 プレビュー</div>
<TextWithImages text={text} images={images} />
</div>
);
}
最終的な動作
✅ 修正後の動作
- 手動スペースがない段落: 自動的に1字下げが適用される
- 手動スペースがある段落: 自動字下げは無効化され、手動スペースのみが表示される(2重字下げ回避)
- 管理画面のプレビュー: 実際の表示と同じ字下げで確認可能
まとめ
この問題を解決するためのポイントは以下の通りです:
- CSS設計: 基本的な字下げスタイルと例外処理の両方を定義
- 条件分岐: 手動スペースの検出による適切なクラス適用
- 一貫性: プレビューと実際の表示で同じコンポーネントを使用
日本語の文章表示では、このような細かな typography の配慮が重要になります。特に教育系アプリケーションでは、正しい日本語表記は必須要件といえるでしょう。
同様の問題に遭遇した方の参考になれば幸いです。
Discussion