• #nuxt
  • #markdown
  • #css
  • #ui
  • #troubleshooting
開発excel-viewer完了

Excel風行番号表示機能の実装状況

そもそもの課題

マークダウンコンテンツの表示に、Excel風の行番号を追加したい。

要件

  • 全てのコンテンツ要素(見出し、段落、リスト、コードブロック、テーブル)に行番号を表示
  • 行番号は小さいグレーの数字で表示
  • 全ての行番号を左端の同じX座標に揃える(最重要)
  • 選択できないようにする
  • スクロールしても見やすいように

クリアできている点

✅ 基本的な行番号表示

  • CSSのcounter-resetcounter-incrementで自動採番
  • ::before擬似要素を使って行番号を表示
  • グレー色(#9ca3af)の小さい文字(0.7rem)で表示
  • user-select: noneで選択不可

✅ 見出しの行番号配置

.doc__body :deep(h1)::before,
.doc__body :deep(h2)::before,
.doc__body :deep(h3)::before {
  position: absolute;
  left: -3rem;
  top: 50%;
  transform: translateY(-50%);
}
  • 見出しは垂直方向中央に配置
  • 左端に固定(left: -3rem

✅ 段落とコードブロックの行番号配置

.doc__body :deep(p)::before,
.doc__body :deep(pre)::before {
  position: absolute;
  left: -3rem;
  top: 0;
}
  • 段落とコードブロックは上端に配置
  • 左端に固定(left: -3rem

✅ コードブロックの言語ラベル競合回避

  • 元々::beforeに言語ラベル(BASH, JSなど)を表示していた
  • ::afterに移動することで、::beforeを行番号に使用可能に

実装の変遷

初期の問題:リスト項目の行番号がずれる

当初、リスト項目にも行番号を表示しようとしたが、以下の問題に直面:

問題:

  • リスト項目(li)の行番号が右にずれる
  • ネストされたリストはさらに右にずれる
  • position: absoluteleftは最も近いposition指定された祖先要素からの相対位置のため、親ul/olのインデントの影響を受ける

試した解決策:

  1. calc()で親のmarginを打ち消す → ネストで失敗
  2. right: calc(100% + 0.5rem)を使用 → リストの幅に依存
  3. position: fixedを使用 → スクロール時に問題

最終的な判断:

  • マークダウンには元々行番号が表示されていない
  • 箇条書きに行番号を付ける必要性は低い
  • 複雑な実装よりも、リスト項目から行番号を削除してシンプル化

最終的な解決策

✅ CSS変数ベースのアプローチ(実装済み)

CSS変数を使ってネストレベルのインデント量を累積計算し、常に絶対位置に行番号を配置する方法を実装。

.doc__body {
  --doc-line-gutter: 3rem;
  --doc-line-indent: 0rem;
}

.doc__body :deep(ul),
.doc__body :deep(ol) {
  --doc-line-indent: calc(var(--doc-line-indent, 0rem) + 1.25rem);
}

.doc__body :deep(li)::before {
  left: calc(var(--doc-line-gutter) * -1 - var(--doc-line-indent, 0rem));
}

この方法により、ネストされたリストでもインデント量を累積計算し、全ての行番号を統一位置(-48px)に配置可能。

✅ さらなるシンプル化:リスト項目から行番号を削除

決定事項:

  • マークダウンには元々行番号が表示されていない
  • 箇条書き(リスト項目)に行番号を付ける必要性は低い
  • リスト項目(li)から行番号を削除してシンプル化

最終仕様:

  • 見出し(h1-h6):行番号表示 ✅
  • 段落(p):行番号表示 ✅
  • コードブロック(pre):行番号表示 ✅
  • テーブル(table):行番号表示 ✅
  • リスト項目(li):行番号なし

実装結果

全ての行番号が-48pxで完璧に揃い、リスト項目は行番号なしでシンプルに表示される。

実装ファイル

apps/web/app/components/DocPage.vue<style scoped>セクション

参考