[{"data":1,"prerenderedAt":385},["ShallowReactive",2],{"content-/2026-04-29-excel-scroll-article-unification":3,"all-pages-for-dir":383,"og-image-/2026-04-29-excel-scroll-article-unification":384},{"id":4,"title":5,"body":6,"category":364,"description":365,"extension":366,"meta":367,"navigation":328,"path":368,"project_name":369,"published":370,"publishedAt":371,"seo":372,"stem":373,"tags":374,"todo":381,"updatedAt":381,"__hash__":382},"pages/2026-04/2026-04-29/excel-scroll-article-unification.md","Excel講座の統合記事パターンを ScrollArticle に共通化した日",{"type":7,"value":8,"toc":353},"minimark",[9,13,16,20,35,38,41,44,47,53,67,71,78,117,120,123,127,134,213,225,232,235,246,250,253,257,264,297,300,312,315,349],[10,11,12],"p",{},"朝、Excel講座の contents.vue に手を入れたところから一日が転がった。冒頭フックに「30分」「3つの習慣」を埋め、第2章で答え合わせをする伏線回収の流れを組み込んだ。short-video-strategy スキルに入っているド素人ホテル再建計画氏の方法論——最初に謎を提示して、本編で正体を一気に明かす——を講座のスクロール体験に移植した形になる。",[10,14,15],{},"書き出してすぐ、伏線と回収が噛み合っていない箇所が浮き上がった。「3つのポイント、その正体」のセクションで章番号を伏字（→第2章で開示）にしておいたのに、本編では別の角度から答えていた。「2つのキー」「データの持ち方」「外側」の3点で受け止め直すよう書き換え、冒頭の問いと末尾の答えがつながるようにした。",[17,18,19],"h2",{"id":19},"画像モーダルとフォントの足回りを整える",[10,21,22,26,27,30,31,34],{},[23,24,25],"code",{},"image-modal.client.ts"," プラグインに ",[23,28,29],{},".scroll-img"," と ",[23,32,33],{},".exercise-img"," のセレクタを追加した。SVGをモーダルで開くと背景が透過して文字が見えなくなっていたので、背景色を白に固定する1行も足した。",[10,36,37],{},"ChatBox と VideoSubtitlePlayer のフォントも、イントロセクションの基準（font-size: 1.125rem / line-height: 2）に揃えた。3つのコンポーネントで微妙に値が違っていて、スクロールするたびに文字の重みが変わっていたのを撤去した形になる。",[17,39,40],{"id":40},"hardware章をスライド型から統合記事型へ",[10,42,43],{},"ここから本題。hardware章は ThinkPad / Monitor / PC の3パートに分かれていて、それぞれが独立したスライドとして表示されていた。1ページに集約する方が読み手の負荷が低いと判断し、article モード（統合記事型）に置き換えた。",[10,45,46],{},"TheaterViewer.vue の article モードには目次がなかったので、h3 を抽出して左サイドに並べる実装を足した。デプロイしてブラウザで開いた瞬間、ユーザーから指摘が飛んできた。",[48,49,50],"blockquote",{},[10,51,52],{},"contents.vue と hardware側で目次レイアウトが違う",[10,54,55,56,59,60,59,63,66],{},"並べて見ると、たしかに目次の位置・幅・スクロール挙動がそれぞれ別物だった。原因を辿ると、両者で ",[23,57,58],{},"scroll-layout"," / ",[23,61,62],{},"scroll-content",[23,64,65],{},"scroll-inner"," の DOM 構造が分岐していた。contents.vue は独自に書いていて、TheaterViewer は別系統の CSS で組まれていた。",[17,68,70],{"id":69},"計画書を書いて-codex-に3回レビューさせた","計画書を書いて Codex に3回レビューさせた",[10,72,73,74,77],{},"その場で直すと別の差異を生むと踏んで、まず ",[23,75,76],{},"memo/2026-04-29/scroll-layout-unification-plan.md"," に計画書を起こした。",[79,80,85],"pre",{"className":81,"code":82,"language":83,"meta":84,"style":84},"language-bash shiki shiki-themes vitesse-light vitesse-light","codex exec -m gpt-5.4 \"このプランをレビューして。瑣末な点へのクソリプはしないで。致命的な点だけ指摘して: ...\"\n","bash","",[23,86,87],{"__ignoreMap":84},[88,89,92,96,100,104,107,111,114],"span",{"class":90,"line":91},"line",1,[88,93,95],{"class":94},"senZ8","codex",[88,97,99],{"class":98},"sdGka"," exec",[88,101,103],{"class":102},"snbK4"," -m",[88,105,106],{"class":98}," gpt-5.4",[88,108,110],{"class":109},"sMJiu"," \"",[88,112,113],{"class":98},"このプランをレビューして。瑣末な点へのクソリプはしないで。致命的な点だけ指摘して: ...",[88,115,116],{"class":109},"\"\n",[10,118,119],{},"3回回した。1回目は「URL hash を更新するか否か」を詰めろと言われた。スクロール連動で hash を書き換えると、戻るボタンが履歴で埋まって機能しなくなる懸念がある。案 X（hash を更新しない方針）を採用した。",[10,121,122],{},"2回目は h2/h3 の id 属性が DOM に正しく付与されるかを確認しろという指摘。MDC レンダラ経由だと id が落ちることがあるので、ScrollArticle 側で明示的に slug を生成して付ける実装に倒した。3回目は実装方針の最終確認だけで、致命的な指摘はなくなった。",[17,124,126],{"id":125},"scrollarticlevue-を切り出す","ScrollArticle.vue を切り出す",[10,128,129,130,133],{},"共通コンポーネントとして ",[23,131,132],{},"ScrollArticle.vue"," を新規作成した。インターフェースはこの形に落ち着いた。",[79,135,139],{"className":136,"code":137,"language":138,"meta":84,"style":84},"language-vue shiki shiki-themes vitesse-light vitesse-light","\u003CScrollArticle\n  :sections=\"sections\"\n  :show-toc=\"true\"\n  @section-enter=\"onSectionEnter\"\n/>\n","vue",[23,140,141,151,171,188,206],{"__ignoreMap":84},[88,142,143,147],{"class":90,"line":91},[88,144,146],{"class":145},"shFtX","\u003C",[88,148,150],{"class":149},"sHkkW","ScrollArticle\n",[88,152,154,157,160,163,166,169],{"class":90,"line":153},2,[88,155,156],{"class":145},"  :",[88,158,159],{"class":94},"sections",[88,161,162],{"class":145},"=",[88,164,165],{"class":145},"\"",[88,167,159],{"class":168},"s4oTP",[88,170,116],{"class":145},[88,172,174,176,179,181,183,186],{"class":90,"line":173},3,[88,175,156],{"class":145},[88,177,178],{"class":94},"show-toc",[88,180,162],{"class":145},[88,182,165],{"class":145},[88,184,185],{"class":149},"true",[88,187,116],{"class":145},[88,189,191,194,197,199,201,204],{"class":90,"line":190},4,[88,192,193],{"class":145},"  @",[88,195,196],{"class":94},"section-enter",[88,198,162],{"class":145},[88,200,165],{"class":145},[88,202,203],{"class":168},"onSectionEnter",[88,205,116],{"class":145},[88,207,209],{"class":90,"line":208},5,[88,210,212],{"class":211},"sG7-3","/>\n",[10,214,215,217,218,59,220,59,222,224],{},[23,216,159],{}," には h2 単位のブロックを配列で渡し、内部で h3 を抽出して目次を作る。contents.vue と TheaterViewer の article モードがこの1コンポーネントを呼ぶ形に置き換わり、",[23,219,58],{},[23,221,62],{},[23,223,65],{}," の3層構造が両者で揃った。",[10,226,227,228,231],{},"deep link をブラウザで踏んで、",[23,229,230],{},"#section-2-1"," のような h3 アンカーまで正しくスクロールすることを確認した。差し替え前は片方の経路でだけ id が消えていた。",[17,233,234],{"id":234},"モバイル幅で出た重複バグ",[10,236,237,238,241,242,245],{},"PC では問題なかったが、Chrome DevTools のモバイルビューに切り替えた瞬間、画面に ScrollArticle と theater-mobile が縦に2つ並んで描画された。CSS のメディアクエリで ",[23,239,240],{},"display: none"," を切り替える側が、共通化の過程で両方残ってしまっていた。条件分岐を ",[23,243,244],{},"v-if"," に寄せて DOM レベルで片方を消すよう直した。",[17,247,249],{"id":248},"stream-deck-も統合記事型に寄せる","stream-deck も統合記事型に寄せる",[10,251,252],{},"勢いで、stream-deck の chapter（si=0〜4）も「1 chapter = 1 統合記事」のフォーマットに集約した。si ごとに画像とテキストが小刻みに分かれていたのを、章単位の長尺ページにまとめた。スクロールが減って、読み手が自分のペースで進めるようになった。",[17,254,256],{"id":255},"simplify-で掃除","/simplify で掃除",[10,258,259,260,263],{},"仕上げに ",[23,261,262],{},"/simplify"," を回して、staging に紛れ込んでいたデバッグPNG10枚を git から外した。ScrollArticle.vue に書き散らした使われていない class も削除した。",[79,265,267],{"className":81,"code":266,"language":83,"meta":84,"style":84},"# 不要なclassを掃除\n- .scroll-debug-overlay\n- .toc-temp-fallback\n- .legacy-section-wrapper\n",[23,268,269,275,283,290],{"__ignoreMap":84},[88,270,271],{"class":90,"line":91},[88,272,274],{"class":273},"sxvE3","# 不要なclassを掃除\n",[88,276,277,280],{"class":90,"line":153},[88,278,279],{"class":94},"-",[88,281,282],{"class":98}," .scroll-debug-overlay\n",[88,284,285,287],{"class":90,"line":173},[88,286,279],{"class":94},[88,288,289],{"class":98}," .toc-temp-fallback\n",[88,291,292,294],{"class":90,"line":190},[88,293,279],{"class":94},[88,295,296],{"class":98}," .legacy-section-wrapper\n",[17,298,299],{"id":299},"用語を固定した",[10,301,302,303,307,308,311],{},"説明するたびに「スクロール型」「統合型」「長尺型」と揺れていたので、コードベースとドキュメントの用語を「",[304,305,306],"strong",{},"統合記事パターン","」と「",[304,309,310],{},"スライドパターン","」の2つに固定した。明日以降、全ページをこの2分類でレビューしていく。",[17,313,314],{"id":314},"明日やること",[316,317,320,331,337,343],"ul",{"className":318},[319],"contains-task-list",[321,322,325,330],"li",{"className":323},[324],"task-list-item",[326,327],"input",{"disabled":328,"type":329},true,"checkbox"," ゲシュタルトの章で使っている静止画を、GIF動画に置き換える（読み手が原理を1サイクル見られるようにする）",[321,332,334,336],{"className":333},[324],[326,335],{"disabled":328,"type":329}," 全ページを統合記事パターン / スライドパターンのどちらに寄せるか棚卸しする",[321,338,340,342],{"className":339},[324],[326,341],{"disabled":328,"type":329}," ScrollArticle のセクション境界アニメーションを 200ms 以内に収めて、スクロール時のカクつきを取る",[321,344,346,348],{"className":345},[324],[326,347],{"disabled":328,"type":329}," stream-deck の chapter ごとに目次の h3 数を5個以下に絞る",[350,351,352],"style",{},"html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}html pre.shiki code .sHkkW, html code.shiki .sHkkW{--shiki-default:#1E754F;--shiki-dark:#1E754F}html pre.shiki code .senZ8, html code.shiki .senZ8{--shiki-default:#59873A;--shiki-dark:#59873A}html pre.shiki code .s4oTP, html code.shiki .s4oTP{--shiki-default:#B07D48;--shiki-dark:#B07D48}html pre.shiki code .sG7-3, html code.shiki .sG7-3{--shiki-default:#393A34;--shiki-dark:#393A34}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .snbK4, html code.shiki .snbK4{--shiki-default:#A65E2B;--shiki-dark:#A65E2B}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}",{"title":84,"searchDepth":153,"depth":153,"links":354},[355,356,357,358,359,360,361,362,363],{"id":19,"depth":153,"text":19},{"id":40,"depth":153,"text":40},{"id":69,"depth":153,"text":70},{"id":125,"depth":153,"text":126},{"id":234,"depth":153,"text":234},{"id":248,"depth":153,"text":249},{"id":255,"depth":153,"text":256},{"id":299,"depth":153,"text":299},{"id":314,"depth":153,"text":314},"diary","Excel講座でフック→伏線→回収構造を導入し、目次レイアウトの差異を ScrollArticle.vue として共通化。Codex 3回レビューを経てモバイル重複バグも潰した一日の記録。","md",{},"/2026-04-29-excel-scroll-article-unification","eurekapu-nuxt4",false,"2026-04-29T00:00:00.000Z",{"title":5,"description":365},"2026-04/2026-04-29/excel-scroll-article-unification",[375,376,377,378,379,380],"Vue","リファクタリング","Codexレビュー","eurekapu","レイアウト","UX",null,"wyDJGVlSBWKde3lSn1fDec2r-nI_LBonOwEF0tTd4_s",[],"https://log.eurekapu.com/favicon.svg",1777533703189]