[{"data":1,"prerenderedAt":1088},["ShallowReactive",2],{"content-/cockpit-responsive-modal-zoom":3,"all-pages-for-dir":1086,"og-image-/cockpit-responsive-modal-zoom":1087},{"id":4,"title":5,"body":6,"category":1068,"description":1069,"extension":1070,"meta":1071,"navigation":665,"ogImage":1072,"path":1073,"project_name":1074,"published":1075,"publishedAt":1076,"seo":1077,"stem":1078,"tags":1079,"todo":1084,"unpublished":1075,"updatedAt":1072,"__hash__":1085},"pages/2026-05/2026-05-09/cockpit-responsive-modal-zoom.md","コクピット財務三表ページのレスポンシブ強化とモーダルの自動ズーム機能",{"type":7,"value":8,"toc":1055},"minimark",[9,14,22,34,48,51,55,62,128,131,133,137,144,211,214,216,220,235,242,244,248,251,258,317,320,322,326,337,359,492,494,498,507,516,725,735,737,741,752,770,777,891,896,898,902,910,916,956,962,964,967,970,990,997,999,1002,1023,1040,1051],[10,11,13],"h2",{"id":12},"_7段階の幅で横スクロールを順に潰した","7段階の幅で横スクロールを順に潰した",[15,16,17,21],"p",{},[18,19,20],"code",{},"/lessons/financial-statements/cockpit-00-summary"," をスマホで開いたら、BS の右列がはみ出して、画面の外に消えた数字を確認するために横スクロールを2回引っ張った。「狭い画面で動くこと」を後回しにしてきたツケを、ここで払うことに決めた。",[15,23,24,25,29,30,33],{},"検証幅は ",[26,27,28],"strong",{},"1920 / 1366 / 1280 / 1100 / 900 / 768 / 375"," の7段階。Chrome DevTools MCP の ",[18,31,32],{},"emulate"," で各幅に切り替えながら、横スクロールバーが画面下に出ない状態を順に作った。",[15,35,36,37,40,41,44,45,47],{},"最初は ",[18,38,39],{},"resize_page"," で幅だけ変える方針で進めたが、",[18,42,43],{},"window.innerWidth"," は変わるのに CSS のメディアクエリが効かない瞬間があって、レイアウトが古い幅のまま固まる挙動が出た。",[18,46,32],{}," でデバイスエミュレーションごと切り替える形に変更したら、メディアクエリも素直に追従した。",[49,50],"hr",{},[10,52,54],{"id":53},"_1920px-wide-section-を1480pxで頭打ちにする","1920px — wide-section を1480pxで頭打ちにする",[15,56,57,58,61],{},"24インチ以上のディスプレイで開くと、wide-section が画面いっぱいに広がって、左右のテーブルが遠く離れて視線が振れた。「広い画面なら何でも広く使えばいい」訳ではなく、",[26,59,60],{},"読む距離を一定に保つ","方針に切り替える。",[63,64,69],"pre",{"className":65,"code":66,"language":67,"meta":68,"style":68},"language-css shiki shiki-themes vitesse-light vitesse-light",".wide-section {\n  max-width: 1480px;\n  margin-inline: auto;\n}\n","css","",[18,70,71,87,108,122],{"__ignoreMap":68},[72,73,76,80,84],"span",{"class":74,"line":75},"line",1,[72,77,79],{"class":78},"shFtX",".",[72,81,83],{"class":82},"s4oTP","wide-section",[72,85,86],{"class":78}," {\n",[72,88,90,94,97,101,105],{"class":74,"line":89},2,[72,91,93],{"class":92},"sz8Xr","  max-width",[72,95,96],{"class":78},":",[72,98,100],{"class":99},"sM54T"," 1480",[72,102,104],{"class":103},"stQ0i","px",[72,106,107],{"class":78},";\n",[72,109,111,114,116,120],{"class":74,"line":110},3,[72,112,113],{"class":92},"  margin-inline",[72,115,96],{"class":78},[72,117,119],{"class":118},"snbK4"," auto",[72,121,107],{"class":78},[72,123,125],{"class":74,"line":124},4,[72,126,127],{"class":78},"}\n",[15,129,130],{},"1480px で頭打ちにして、それ以上の幅では中央に寄せる。1920px のディスプレイでは左右に220pxずつ余白が出るが、テーブルの数字が視野角に収まる側を優先した。",[49,132],{},[10,134,136],{"id":135},"_375px-bsの折り返しがオーバーラップした","375px — BSの折り返しがオーバーラップした",[15,138,139,140,143],{},"モバイル幅で BS を開くと、資産側と負債純資産側が横並びのまま縮んで、テーブル内のテキストが折り返して",[26,141,142],{},"隣の列に重なった","。「狭い画面では2列を諦めて1列にする」が正解。",[63,145,147],{"className":65,"code":146,"language":67,"meta":68,"style":68},"@media (max-width: 480px) {\n  .bs-grid {\n    grid-template-columns: 1fr;\n  }\n}\n",[18,148,149,176,186,201,206],{"__ignoreMap":68},[72,150,151,154,158,161,164,166,169,171,174],{"class":74,"line":75},[72,152,153],{"class":78},"@",[72,155,157],{"class":156},"sHkkW","media",[72,159,160],{"class":78}," (",[72,162,163],{"class":92},"max-width",[72,165,96],{"class":78},[72,167,168],{"class":99}," 480",[72,170,104],{"class":103},[72,172,173],{"class":78},")",[72,175,86],{"class":78},[72,177,178,181,184],{"class":74,"line":89},[72,179,180],{"class":78},"  .",[72,182,183],{"class":82},"bs-grid",[72,185,86],{"class":78},[72,187,188,191,193,196,199],{"class":74,"line":110},[72,189,190],{"class":92},"    grid-template-columns",[72,192,96],{"class":78},[72,194,195],{"class":99}," 1",[72,197,198],{"class":103},"fr",[72,200,107],{"class":78},[72,202,203],{"class":74,"line":124},[72,204,205],{"class":78},"  }\n",[72,207,209],{"class":74,"line":208},5,[72,210,127],{"class":78},[15,212,213],{},"資産明細 → 負債純資産明細 → 合計行、の順に縦に積み重ねる構造に変更。スマホで開いたとき、上から下にスクロールすれば全数字が見える。横スクロールは消えた。",[49,215],{},[10,217,219],{"id":218},"statements-grid-に-ss-が入っていなかった","statements-grid に S/S が入っていなかった",[15,221,222,223,226,227,230,231,234],{},"狭幅版（1280px以下）の ",[18,224,225],{},"statements-grid"," の grid-template-areas を確認したら、",[18,228,229],{},"bs / pl / cs"," の3エリアしか定義されていない。S/S を追加した時に",[26,232,233],{},"狭幅側だけ追従漏れ","していた。",[15,236,237,238,241],{},"広幅版では4表が見えるのに、狭幅版に切り替えた瞬間 S/S が消える挙動が出ていた。grid-template-areas に ",[18,239,240],{},"ss"," エリアを追加し、各幅でレイアウト先を再定義。",[49,243],{},[10,245,247],{"id":246},"モーダル拡大時左ペインの下に空白が伸びていた","モーダル拡大時、左ペインの下に空白が伸びていた",[15,249,250],{},"「拡大ボタン」でモーダルを開くと、左ペイン（テーブル側）の下に画面1〜2画面分の空白が伸びて、右ペイン（解説側）と高さが揃わなかった。",[15,252,253,254,257],{},"調べると、左ペイン側に ",[18,255,256],{},"min-height: 100dvh"," が常時掛かっていた。デスクトップでは右ペインに合わせて高さを揃えたいが、モバイルでは内容に応じた自然高で良い。",[63,259,261],{"className":65,"code":260,"language":67,"meta":68,"style":68},"@media (min-width: 769px) {\n  .modal-left {\n    min-height: 100dvh;\n  }\n}\n",[18,262,263,285,294,309,313],{"__ignoreMap":68},[72,264,265,267,269,271,274,276,279,281,283],{"class":74,"line":75},[72,266,153],{"class":78},[72,268,157],{"class":156},[72,270,160],{"class":78},[72,272,273],{"class":92},"min-width",[72,275,96],{"class":78},[72,277,278],{"class":99}," 769",[72,280,104],{"class":103},[72,282,173],{"class":78},[72,284,86],{"class":78},[72,286,287,289,292],{"class":74,"line":89},[72,288,180],{"class":78},[72,290,291],{"class":82},"modal-left",[72,293,86],{"class":78},[72,295,296,299,301,304,307],{"class":74,"line":110},[72,297,298],{"class":92},"    min-height",[72,300,96],{"class":78},[72,302,303],{"class":99}," 100",[72,305,306],{"class":103},"dvh",[72,308,107],{"class":78},[72,310,311],{"class":74,"line":124},[72,312,205],{"class":78},[72,314,315],{"class":74,"line":208},[72,316,127],{"class":78},[15,318,319],{},"デスクトップだけに条件付けしたら、モバイルの空白が消えた。",[49,321],{},[10,323,325],{"id":324},"ヘッダー横に拡大縮小ボタンを追加","ヘッダー横に拡大/縮小ボタンを追加",[15,327,328,329,332,333,336],{},"モーダル拡大中、テーブルの数字を細かく見たい時にブラウザのズーム（Ctrl+",[18,330,331],{},"+","）を使うと、モーダル外の背景までズームしてレイアウトが崩れる。",[26,334,335],{},"モーダル内だけ","でズームを完結させたい。",[15,338,339,340,343,344,347,348,350,351,354,355,358],{},"ヘッダー右側、",[18,341,342],{},"×"," 閉じるボタンの隣に ",[18,345,346],{},"−"," と ",[18,349,331],{}," のボタンを並べた。クリックで ",[18,352,353],{},"modalZoom"," の値を 0.1 刻みで上下させ、CSS の ",[18,356,357],{},"zoom"," プロパティに反映する。",[63,360,364],{"className":361,"code":362,"language":363,"meta":68,"style":68},"language-ts shiki shiki-themes vitesse-light vitesse-light","const modalZoom = ref(1)\nconst zoomIn = () => modalZoom.value = Math.min(2, modalZoom.value + 0.1)\nconst zoomOut = () => modalZoom.value = Math.max(0.5, modalZoom.value - 0.1)\n","ts",[18,365,366,389,444],{"__ignoreMap":68},[72,367,368,371,373,376,380,383,386],{"class":74,"line":75},[72,369,370],{"class":103},"const ",[72,372,353],{"class":82},[72,374,375],{"class":78}," =",[72,377,379],{"class":378},"senZ8"," ref",[72,381,382],{"class":78},"(",[72,384,385],{"class":99},"1",[72,387,388],{"class":78},")\n",[72,390,391,393,396,398,401,404,407,409,412,414,417,419,422,424,427,430,432,434,436,439,442],{"class":74,"line":89},[72,392,370],{"class":103},[72,394,395],{"class":378},"zoomIn",[72,397,375],{"class":78},[72,399,400],{"class":78}," ()",[72,402,403],{"class":78}," =>",[72,405,406],{"class":82}," modalZoom",[72,408,79],{"class":78},[72,410,411],{"class":82},"value",[72,413,375],{"class":78},[72,415,416],{"class":82}," Math",[72,418,79],{"class":78},[72,420,421],{"class":378},"min",[72,423,382],{"class":78},[72,425,426],{"class":99},"2",[72,428,429],{"class":78},",",[72,431,406],{"class":82},[72,433,79],{"class":78},[72,435,411],{"class":82},[72,437,438],{"class":103}," + ",[72,440,441],{"class":99},"0.1",[72,443,388],{"class":78},[72,445,446,448,451,453,455,457,459,461,463,465,467,469,472,474,477,479,481,483,485,488,490],{"class":74,"line":110},[72,447,370],{"class":103},[72,449,450],{"class":378},"zoomOut",[72,452,375],{"class":78},[72,454,400],{"class":78},[72,456,403],{"class":78},[72,458,406],{"class":82},[72,460,79],{"class":78},[72,462,411],{"class":82},[72,464,375],{"class":78},[72,466,416],{"class":82},[72,468,79],{"class":78},[72,470,471],{"class":378},"max",[72,473,382],{"class":78},[72,475,476],{"class":99},"0.5",[72,478,429],{"class":78},[72,480,406],{"class":82},[72,482,79],{"class":78},[72,484,411],{"class":82},[72,486,487],{"class":103}," - ",[72,489,441],{"class":99},[72,491,388],{"class":78},[49,493],{},[10,495,497],{"id":496},"開いた瞬間ビューポートに自動でフィットさせる","開いた瞬間、ビューポートに自動でフィットさせる",[15,499,500,503,504,506],{},[18,501,502],{},"+/−"," ボタンで手調整できる前提を作った後、「最初に開いた瞬間にビューポート全体が画面に収まる」初期値が欲しくなった。手で何回も ",[18,505,346],{}," を押させるのは負け筋だ。",[15,508,509,512,513,515],{},[18,510,511],{},"applyFitZoom"," 関数を追加して、モーダルを開いた瞬間とウィンドウリサイズ時に呼び出す。コンテンツの実寸とビューポートサイズから、縦横どちらも収まる倍率を計算して ",[18,514,353],{}," に代入する。",[63,517,519],{"className":361,"code":518,"language":363,"meta":68,"style":68},"const applyFitZoom = () => {\n  const content = modalContentRef.value\n  if (!content) return\n  const ratioW = window.innerWidth / content.scrollWidth\n  const ratioH = window.innerHeight / content.scrollHeight\n  modalZoom.value = Math.min(ratioW, ratioH, 1)\n}\n\nonMounted(() => {\n  applyFitZoom()\n  window.addEventListener('resize', applyFitZoom)\n})\n",[18,520,521,535,553,570,597,622,655,660,667,680,689,719],{"__ignoreMap":68},[72,522,523,525,527,529,531,533],{"class":74,"line":75},[72,524,370],{"class":103},[72,526,511],{"class":378},[72,528,375],{"class":78},[72,530,400],{"class":78},[72,532,403],{"class":78},[72,534,86],{"class":78},[72,536,537,540,543,545,548,550],{"class":74,"line":89},[72,538,539],{"class":103},"  const ",[72,541,542],{"class":82},"content",[72,544,375],{"class":78},[72,546,547],{"class":82}," modalContentRef",[72,549,79],{"class":78},[72,551,552],{"class":82},"value\n",[72,554,555,558,560,563,565,567],{"class":74,"line":110},[72,556,557],{"class":156},"  if",[72,559,160],{"class":78},[72,561,562],{"class":103},"!",[72,564,542],{"class":82},[72,566,173],{"class":78},[72,568,569],{"class":156}," return\n",[72,571,572,574,577,579,582,584,587,590,592,594],{"class":74,"line":124},[72,573,539],{"class":103},[72,575,576],{"class":82},"ratioW",[72,578,375],{"class":78},[72,580,581],{"class":82}," window",[72,583,79],{"class":78},[72,585,586],{"class":82},"innerWidth",[72,588,589],{"class":103}," / ",[72,591,542],{"class":82},[72,593,79],{"class":78},[72,595,596],{"class":82},"scrollWidth\n",[72,598,599,601,604,606,608,610,613,615,617,619],{"class":74,"line":208},[72,600,539],{"class":103},[72,602,603],{"class":82},"ratioH",[72,605,375],{"class":78},[72,607,581],{"class":82},[72,609,79],{"class":78},[72,611,612],{"class":82},"innerHeight",[72,614,589],{"class":103},[72,616,542],{"class":82},[72,618,79],{"class":78},[72,620,621],{"class":82},"scrollHeight\n",[72,623,625,628,630,632,634,636,638,640,642,644,646,649,651,653],{"class":74,"line":624},6,[72,626,627],{"class":82},"  modalZoom",[72,629,79],{"class":78},[72,631,411],{"class":82},[72,633,375],{"class":78},[72,635,416],{"class":82},[72,637,79],{"class":78},[72,639,421],{"class":378},[72,641,382],{"class":78},[72,643,576],{"class":82},[72,645,429],{"class":78},[72,647,648],{"class":82}," ratioH",[72,650,429],{"class":78},[72,652,195],{"class":99},[72,654,388],{"class":78},[72,656,658],{"class":74,"line":657},7,[72,659,127],{"class":78},[72,661,663],{"class":74,"line":662},8,[72,664,666],{"emptyLinePlaceholder":665},true,"\n",[72,668,670,673,676,678],{"class":74,"line":669},9,[72,671,672],{"class":378},"onMounted",[72,674,675],{"class":78},"(()",[72,677,403],{"class":78},[72,679,86],{"class":78},[72,681,683,686],{"class":74,"line":682},10,[72,684,685],{"class":378},"  applyFitZoom",[72,687,688],{"class":78},"()\n",[72,690,692,695,697,700,702,706,710,712,714,717],{"class":74,"line":691},11,[72,693,694],{"class":82},"  window",[72,696,79],{"class":78},[72,698,699],{"class":378},"addEventListener",[72,701,382],{"class":78},[72,703,705],{"class":704},"sMJiu","'",[72,707,709],{"class":708},"sdGka","resize",[72,711,705],{"class":704},[72,713,429],{"class":78},[72,715,716],{"class":82}," applyFitZoom",[72,718,388],{"class":78},[72,720,722],{"class":74,"line":721},12,[72,723,724],{"class":78},"})\n",[15,726,727,728,730,731,734],{},"最初は縦方向のフィットだけ計算していたが、横長コンテンツで画面右にはみ出す事象が出たので、",[18,729,576],{}," も足して ",[18,732,733],{},"Math.min"," で小さい方を採る形に直した。",[49,736],{},[10,738,740],{"id":739},"css-zoom-下で-svg-オーバーレイがずれた","CSS zoom 下で SVG オーバーレイがずれた",[15,742,743,744,747,748,751],{},"ここで一番ハマった問題。",[18,745,746],{},"zoom: 0.7"," を当てた状態で BS と CS を結ぶ SVG コネクターを描くと、線の端が",[26,749,750],{},"テーブルの数字から数十px ずれる","。zoom が等倍（1.0）に戻すと、ずれは消える。",[15,753,754,755,758,759,761,762,765,766,769],{},"原因を追ったら、SVG の path 座標は ",[18,756,757],{},"getBoundingClientRect()"," で取得したアンカー座標から計算している。",[18,760,757],{}," は ",[26,763,764],{},"CSS zoom 後の実画面座標","を返すのに対し、SVG の ",[18,767,768],{},"viewBox"," を未指定にしていたため、SVG 側は内部的に等倍前提の座標系で描画していた。両者の座標系がズレて、線の端が数字から離れた。",[15,771,772,773,776],{},"解決策は SVG に ",[26,774,775],{},"動的な viewBox"," を付けて、コンテナの実寸（CSS zoom 前のサイズ）に座標系を揃えること。",[63,778,780],{"className":361,"code":779,"language":363,"meta":68,"style":68},"// コンテナの「論理サイズ」（zoom無視）でviewBoxを設定\nconst svgViewBox = computed(() => {\n  const w = containerRef.value?.offsetWidth ?? 0\n  const h = containerRef.value?.offsetHeight ?? 0\n  return `0 0 ${w} ${h}`\n})\n",[18,781,782,788,806,834,858,887],{"__ignoreMap":68},[72,783,784],{"class":74,"line":75},[72,785,787],{"class":786},"sxvE3","// コンテナの「論理サイズ」（zoom無視）でviewBoxを設定\n",[72,789,790,792,795,797,800,802,804],{"class":74,"line":89},[72,791,370],{"class":103},[72,793,794],{"class":82},"svgViewBox",[72,796,375],{"class":78},[72,798,799],{"class":378}," computed",[72,801,675],{"class":78},[72,803,403],{"class":78},[72,805,86],{"class":78},[72,807,808,810,813,815,818,820,822,825,828,831],{"class":74,"line":110},[72,809,539],{"class":103},[72,811,812],{"class":82},"w",[72,814,375],{"class":78},[72,816,817],{"class":82}," containerRef",[72,819,79],{"class":78},[72,821,411],{"class":82},[72,823,824],{"class":78},"?.",[72,826,827],{"class":82},"offsetWidth",[72,829,830],{"class":103}," ?? ",[72,832,833],{"class":99},"0\n",[72,835,836,838,841,843,845,847,849,851,854,856],{"class":74,"line":124},[72,837,539],{"class":103},[72,839,840],{"class":82},"h",[72,842,375],{"class":78},[72,844,817],{"class":82},[72,846,79],{"class":78},[72,848,411],{"class":82},[72,850,824],{"class":78},[72,852,853],{"class":82},"offsetHeight",[72,855,830],{"class":103},[72,857,833],{"class":99},[72,859,860,863,866,869,872,874,877,880,882,884],{"class":74,"line":208},[72,861,862],{"class":156},"  return",[72,864,865],{"class":704}," `",[72,867,868],{"class":708},"0 0 ",[72,870,871],{"class":156},"${",[72,873,812],{"class":708},[72,875,876],{"class":156},"}",[72,878,879],{"class":156}," ${",[72,881,840],{"class":708},[72,883,876],{"class":156},[72,885,886],{"class":704},"`\n",[72,888,889],{"class":74,"line":624},[72,890,724],{"class":78},[15,892,893,895],{},[18,894,827],{}," は zoom の影響を受けない論理サイズを返すので、これを viewBox の幅として渡せば SVG 側の座標系と path 計算側の座標系が一致する。zoom がいくつでも、線の端が数字に正確に当たる。",[49,897],{},[10,899,901],{"id":900},"modalzoom-変更時にオーバーレイ再計算","modalZoom 変更時にオーバーレイ再計算",[15,903,904,906,907,909],{},[18,905,502],{}," ボタンで ",[18,908,353],{}," を変えた瞬間、SVG オーバーレイが古い座標のまま残って、線が数字からずれて見えた。",[15,911,912,913,915],{},"オーバーレイの再計算 trigger に ",[18,914,353],{}," を含めて、ズーム変更ごとに座標を再評価する形に変更。",[63,917,919],{"className":361,"code":918,"language":363,"meta":68,"style":68},"watch([entryId, modalZoom], () => {\n  recalcOverlayPositions()\n})\n",[18,920,921,945,952],{"__ignoreMap":68},[72,922,923,926,929,932,934,936,939,941,943],{"class":74,"line":75},[72,924,925],{"class":378},"watch",[72,927,928],{"class":78},"([",[72,930,931],{"class":82},"entryId",[72,933,429],{"class":78},[72,935,406],{"class":82},[72,937,938],{"class":78},"],",[72,940,400],{"class":78},[72,942,403],{"class":78},[72,944,86],{"class":78},[72,946,947,950],{"class":74,"line":89},[72,948,949],{"class":378},"  recalcOverlayPositions",[72,951,688],{"class":78},[72,953,954],{"class":74,"line":110},[72,955,724],{"class":78},[15,957,958,959,961],{},"これで ",[18,960,502],{}," を押すたびに線が追従するようになった。",[49,963],{},[10,965,966],{"id":966},"コミット",[15,968,969],{},"担当分のみ4ファイル、162行追加でコミット。",[971,972,973,980],"ul",{},[974,975,976,979],"li",{},[18,977,978],{},"1eea1fb"," レスポンシブ調整・S/S狭幅版バグ修正・左ペイン min-height 条件付け",[974,981,982,985,986,989],{},[18,983,984],{},"4a67b35"," モーダル ",[18,987,988],{},"±"," ズーム・applyFitZoom・SVG viewBox 動的化",[15,991,992,993,996],{},"作業ログは ",[18,994,995],{},"memo/2026-05-09/cockpit-responsive-and-modal-zoom-worklog.md"," に残した。",[49,998],{},[10,1000,1001],{"id":1001},"学びメモ",[15,1003,1004,1009,1010,1012,1013,1016,1017,1019,1020,1022],{},[26,1005,1006,1008],{},[18,1007,39],{}," だけだとメディアクエリが追従しないことがあった。"," Chrome DevTools MCP で幅を変えた時、",[18,1011,43],{}," の値は更新されるのに ",[18,1014,1015],{},"@media (max-width: 480px)"," のスタイルが古い幅のまま固まる瞬間が出た。",[18,1018,32],{}," でデバイスエミュレーションごと切り替えに変えたら、メディアクエリも一緒に追従した。次から狭幅検証では最初から ",[18,1021,32],{}," を使う。",[15,1024,1025,1028,1029,1031,1032,1035,1036,1039],{},[26,1026,1027],{},"CSS zoom と座標計算ライブラリの相性は悪い。"," ",[18,1030,757],{}," は zoom 後の座標を返すのに、SVG の path は viewBox なしでは等倍前提で描画される。両者の座標系を揃える方法は ",[26,1033,1034],{},"SVG 側に動的 viewBox を当てる","のが一番素直だった。",[18,1037,1038],{},"transform: scale()"," で代替する案も検討したが、子要素のレイアウト幅が変わらない問題が出るので zoom を維持した。",[15,1041,1042,1028,1045,1047,1048,1050],{},[26,1043,1044],{},"「最初に開いた瞬間に最適な状態」を作るのは ±ボタン2個分の手間を消す。",[18,1046,511],{}," を入れる前は、ユーザーが毎回 ",[18,1049,346],{}," を3〜4回押してフィットさせていた。自動フィット1行が、ユーザーの指の動きを毎回4回ずつ削った。",[1052,1053,1054],"style",{},"html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}html pre.shiki code .s4oTP, html code.shiki .s4oTP{--shiki-default:#B07D48;--shiki-dark:#B07D48}html pre.shiki code .sz8Xr, html code.shiki .sz8Xr{--shiki-default:#998418;--shiki-dark:#998418}html pre.shiki code .sM54T, html code.shiki .sM54T{--shiki-default:#2F798A;--shiki-dark:#2F798A}html pre.shiki code .stQ0i, html code.shiki .stQ0i{--shiki-default:#AB5959;--shiki-dark:#AB5959}html pre.shiki code .snbK4, html code.shiki .snbK4{--shiki-default:#A65E2B;--shiki-dark:#A65E2B}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 .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 .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}",{"title":68,"searchDepth":89,"depth":89,"links":1056},[1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067],{"id":12,"depth":89,"text":13},{"id":53,"depth":89,"text":54},{"id":135,"depth":89,"text":136},{"id":218,"depth":89,"text":219},{"id":246,"depth":89,"text":247},{"id":324,"depth":89,"text":325},{"id":496,"depth":89,"text":497},{"id":739,"depth":89,"text":740},{"id":900,"depth":89,"text":901},{"id":966,"depth":89,"text":966},{"id":1001,"depth":89,"text":1001},"dev","eurekapu-nuxt4のcockpit-00-summaryで7段階の幅（1920/1366/1280/1100/900/768/375）を順に検証し、横スクロールを潰した。1920px は wide-section を1480pxで頭打ちにしてセンタリング、375px はBSの折り返し被りを1列積みで解消。さらにモーダル拡大時に自動でビューポートにフィットする applyFitZoom と、ヘッダー横の±ズームボタンを追加。CSS zoom 配下で SVG オーバーレイ座標がずれる問題は、SVG に動的 viewBox を当てて座標系を揃えて解決した。","md",{},null,"/cockpit-responsive-modal-zoom","eurekapu-nuxt4",false,"2026-05-09T00:00:00.000Z",{"title":5,"description":1069},"2026-05/2026-05-09/cockpit-responsive-modal-zoom",[1080,1081,1082,1083],"Vue","レスポンシブ","Chrome DevTools MCP","UX改善","memo","Qe4xk-QVfmO3dbDb8W8sck4E5oy8M1C90f_fkGE99Ys",[],"https://log.eurekapu.com/og/blog/cockpit-responsive-modal-zoom.png?v=2026-05-09T00%3A00%3A00.000Z&title=%E3%82%B3%E3%82%AF%E3%83%94%E3%83%83%E3%83%88%E8%B2%A1%E5%8B%99%E4%B8%89%E8%A1%A8%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B7%E3%83%96%E5%BC%B7%E5%8C%96%E3%81%A8%E3%83%A2%E3%83%BC%E3%83%80%E3%83%AB%E3%81%AE%E8%87%AA%E5%8B%95%E3%82%BA%E3%83%BC%E3%83%A0%E6%A9%9F%E8%83%BD&author=Kei%20Komatsu&sig=a09ffe9450aabd75",1782528834427]