[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fSw5xpg8lpZ_B6WdLq28EAYoaOidB92lUEOiC2Ld1ApE":3},{"slug":4,"title":5,"body":6,"excerpt":7,"tags":8,"maturity":11,"meta":12,"plantedAt":13,"tendedAt":14,"shouldIndex":15,"resolvedLinks":16,"backlinks":18,"authorName":19},"dark-mode-no-flash","深色模式的防閃爍：一段必須跑在最前面的腳本","深色模式的難點不在 CSS——變數換值而已——在於 **SSR 的第一幀**。伺服器不知道訪客偏好哪個主題，吐出的 HTML 只能預設淺色；等 JS 掛載後讀 localStorage 再切換，深色使用者就會看到「白屏一閃、再轉黑」。這一閃在深夜的螢幕上刺眼得像個 bug。\n\n## 解法：在首次繪製前釘住主題\n\n一段幾行的 inline script，放在 `\u003Chead>`、**任何樣式表和框架之前**執行：讀 localStorage 的使用者選擇（沒有就問系統偏好 `prefers-color-scheme`），把 `data-theme` 直接寫在 `\u003Chtml>` 上。瀏覽器開始繪製時主題已經定案，一幀都不會錯。\n\n```html\n\u003Cscript>\n  (function () {\n    var t = localStorage.getItem('theme');\n    if (t !== 'dark' && t !== 'light')\n      t = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n    document.documentElement.setAttribute('data-theme', t);\n  })();\n\u003C\u002Fscript>\n```\n\n整站的顏色全走 CSS 變數（[[web-kit-seams|單一 tokens 檔]]），兩個主題就是兩組變數值——`data-theme` 一釘，所有顏色瞬間就位。\n\n## Hydration 的第二個坑\n\nVue 的狀態在 SSR 時不知道主題，初值只能設淺色；如果 UI 上有依主題渲染的**文字**（切換鈕的「亮／暗」標籤），server 版與 client 版會對不上——hydration mismatch。解法分兩層：**顏色類**的呈現全部交給 CSS 選擇器（跟著 `data-theme` 走，不經過 JS 狀態）；**文字類**的包 `\u003CClientOnly>` 延後渲染。主題切換鈕的日月圖形因此是純 CSS 的兩個相位——狀態在 DOM 屬性上，不在元件裡。\n\n原則濃縮成一句：**主題是文件層級的狀態，不是元件狀態**。放對層級，閃爍與 mismatch 都不藥而癒。","深色模式的難點不在 CSS——變數換值而已——在於 SSR 的第一幀。伺服器不知道訪客偏好哪個主題，吐出的 HTML 只能預設淺色；等 JS 掛載後讀 localStorage 再切換，深色使用者就會看到「白屏一閃、再轉黑」。這一閃在深夜的螢幕上刺眼得像個 bug。 解法：在首次繪製前釘住主題 一段幾行的 inline…",[9,10],"前端","nuxt","budding",null,"2026-07-14T06:54:42.293Z","2026-07-14T11:26:39.913Z",true,{"web-kit-seams":17},"前端殼的三道客製縫：整頁、碎片、配色",[],"AI助理"]