• #レイアウト
  • #ミラーカラム
  • #スクロール
  • #Vue
  • #SVG
  • #UX
開発eurekapu-nuxt4メモ

ミラーカラムからスクロール形式へ — 教育コンテンツのレイアウト変換

CF計算書の基礎知識ページ(boki3/ch0)で、ミラーカラムレイアウトのトピック列を消してスクロール形式に差し替えた。ユーザーからスクリーンショットが3回飛んできて、そのたびにレイアウトを書き直した。最終的にExcel基礎講座にも横展開し、Ch1以降の全チャプターにも適用した。

発端

ミラーカラムレイアウトは「セクション → チャプター → トピック → スライド」の4列構造で、1枚ずつスライドを切り替えるUIになっている。スライド形式だとチャートの説明が冗長になり、テキストが断片化する。ユーザーから「普通のスクロールコンテンツにしたい」という要望が出た。

試行錯誤のプロセス

1回目: レイアウト全体をstepsレイアウトに差し替え

最初はstepsレイアウト(左サイドバー + スクロールコンテンツ)に丸ごと置き換えた。ユーザーの反応は「違う」。左側のセクション列・チャプター列はミラーカラムの見た目を維持してほしい、中身だけスクロールにしてほしい、とのこと。

2回目: MillerViewerのスロットでコンテンツ差し替え

MillerViewerコンポーネントのslotを使い、右のコンテンツ領域(Column 4)だけをスクロールHTMLに差し替えた。これもユーザーの意図とは違った。「チャプター列は残していいけど、トピック列をスクロールでまとめてほしい」。

3回目: singleSectionModeでトピック列を消す

MillerViewerは、各チャプターのセクション数が1の場合にトピック列を自動非表示にするsingleSectionModeを持っていた。これを利用し、データ側で各チャプターのセクションを1つに統合。トピック列が消え、セクション列 + チャプター列 + スクロールコンテンツの3列構成になった。ここでユーザーからOKが出た。

実装した変更

stepsレイアウトのスタイル適用

steps側のch0では、コンテンツ幅・行間・段落間隔をすでに調整してあった。boki3/ch0にも同じスタイルを移植した。

/* stepsレイアウトから移植した主要スタイル */
.scroll-inner h2 { font-size: 1.5rem; margin: 2.5rem 0 1rem; }
.scroll-inner h3 { font-size: 1.25rem; margin: 2rem 0 0.75rem; }
.scroll-inner p { line-height: 1.8; margin-bottom: 1rem; }

右サイドTOC追加

スクロールコンテンツの右側に目次(Table of Contents)を追加。セクション見出しへのアンカーリンクで、スクロール位置を把握できるようにした。

セクション・チャプタータイトルの整理

タイトルから冗長な「ch」「0-1」などの接頭辞を削除し、数字だけに整理した。3ファイル(TOC、millerデータ、ch0.vue内のscrollChapters)を一括修正。

SVGのwidth="100%"追加

SVGのルート<svg>タグにwidth属性がなく、ブラウザがintrinsic sizeで小さく描画する問題があった。初期読み込み時に画像が一瞬小さく表示され、リフロー後に正しいサイズになる。全SVGにwidth="100%"を追加して解消した。

steps側のコンテンツをboki3/ch0に流し込み

steps/ch0-1とch0-2の方が内容が充実していた。基礎知識の差分を埋めるため、steps側のテキスト・キーワード・SVG図をboki3/ch0に流し込んだ。会計基準の引用(BookkeepingStandardRefコンポーネント)も3箇所追加した。

Ch1以降のスクロール変換

Ch1(借入金ライフサイクル)もch0と同じパターンで変換。millerデータのテキストをHTML化し、singleSectionModeでトピック列を消し、右TOCを追加した。

チャプター切替時のスクロール位置リセット

チャプターを矢印キーで切り替えると、スクロール位置が前のチャプターの下部のまま残る問題があった。nextTickだけではブラウザのスクロール復元に負けるため、flush: 'post' + requestAnimationFrameの二段構えで対処した。

watch(chapterIdx, () => {
  // flush: 'post' でDOM更新後に発火
  nextTick(() => {
    // requestAnimationFrame でブラウザのレイアウト後に再度リセット
    requestAnimationFrame(() => {
      scrollContainer.value?.scrollTo({ top: 0 })
    })
  })
}, { flush: 'post' })

Excel基礎講座への横展開

contents.vueのスクロール変換

boki3/ch0と同じ手法でExcel基礎講座のcontents.vueもスクロール形式に変換した。こちらはチャプター列も消す(TOCのchildrenを1つに統合してnoChapterColを発動)仕様。セクション列 + スクロールコンテンツ + 右TOCの3列になった。

top.vueも同様にスクロール変換

top.vueも同じパターンで変換。「テキスト→図」の順を全セクションで統一した。

SVG図のトーン&マナー統一

contents.vueの3つのSVG図を、svg-diagramスキルのデザインルール(グレー8段階、フォントサイズ規則)に合わせて作り直した。

コンテンツ一覧テーブルの追加

「作業スピードを上げる3つの方法」ページの各セクション(基本機能・ショートカット・関数)の直下に、チャプター名(左・rowspan結合)とサブトピック(右)の階層テーブルを追加。全サブトピックにクエリパラメータ付きリンクを設定し、該当ページに直接遷移できるようにした。

振り返り

ミラーカラムのsingleSectionModeという既存機能を見つけたことで、コンポーネントを書き直さずにトピック列だけを消せた。ユーザーの要望を3回聞き直す過程で「セクション列とチャプター列は残す」「トピック列だけスクロール化する」という仕様が固まった。1回目で正解を出そうとするより、スクリーンショットを見ながら方向を修正するフローの方が、手戻りが小さく済む。

flush: 'post' + requestAnimationFrameの二段構えは、Vue + ブラウザのスクロール復元が競合する場面で使い回せるパターンとして覚えておく。