[{"data":1,"prerenderedAt":1162},["ShallowReactive",2],{"search-dialog-posts":3,"post-\u002Fposts\u002Fhtml-meta-boilerplate-today":316,"post-surround-\u002Fposts\u002Fhtml-meta-boilerplate-today":1155},[4,12,21,29,37,47,57,67,77,85,96,104,112,121,129,137,147,155,164,173,181,188,195,207,215,224,236,241,247,254,260,266,272,279,286,291,298,304,310],{"path":5,"title":6,"description":7,"category":8,"tags":9},"\u002Fposts\u002Fgit-commands-cheatsheet","Git 常用指令與終端機筆記","2018 年整理的 Git 學習筆記，包含基本終端機指令、Git 操作、分支、reset、SourceTree、Vim 與一些常用設定。","notes",[10,11],"Git","版本控制",{"path":13,"title":14,"description":15,"category":16,"tags":17},"\u002Fposts\u002Fjs-prototype-inheritance","JavaScript 物件導向：建構函式、prototype 與繼承三步驟","整理 ES5 時代用建構函式加 prototype 手刻物件導向的完整流程——封裝屬性與方法、共用函式為什麼要掛在 prototype 上、以及繼承的三個步驟（call 借用母類別建構子、Object.create 接上原型鏈、修正 constructor 指向），並附上物件縮寫與計算屬性這兩個順便學到的 ES6 寫法。","frontend",[18,19,20],"JavaScript","物件導向","prototype",{"path":22,"title":23,"description":24,"category":16,"tags":25},"\u002Fposts\u002Fflexbox-notes","Flexbox 對齊與彈性尺寸筆記","記錄 Flexbox 的主軸與交錯軸對齊、flex-direction、flex-wrap，以及 flex-grow、flex-shrink、flex-basis 等常用屬性。",[26,27,28],"CSS","Flexbox","版面",{"path":30,"title":31,"description":32,"category":16,"tags":33},"\u002Fposts\u002Fscreen-orientation-api","screen.orientation：現在判斷與鎖定螢幕方向的做法","整理現在 JavaScript 判斷螢幕方向的方式，包括 screen.orientation、change 事件與 lock()，並說明 RWD 使用 matchMedia 或 CSS orientation 會更適合。",[18,34,35,36],"RWD","螢幕方向","PWA",{"path":38,"title":39,"description":40,"category":16,"tags":41},"\u002Fposts\u002Fpinia-vuex-state-management","Pinia \u002F Vuex 狀態管理及差異","紀錄 Vuex、Pinia 的狀態管理概念、差異與用法。",[42,43,44,45,46],"Pinia","Vuex","Vue3","狀態管理","Nuxt",{"path":48,"title":49,"description":50,"category":8,"tags":51},"\u002Fposts\u002Fpyenv-python-version-management","Python 多版本管理 pyenv","紀錄 macOS 與 Windows 使用 pyenv 管理 Python 版本的方式。",[52,53,54,55,56],"Python","pyenv","macOS","Windows","版本管理",{"path":58,"title":59,"description":60,"category":8,"tags":61},"\u002Fposts\u002Fnpm-publish-vue2-component","npm 發布流程","紀錄 Vue 2 元件的 npm 發布、dist-tag 與打包流程。",[62,63,64,65,66],"npm","Vue2","Vue CLI","套件發布","MIT",{"path":68,"title":69,"description":70,"category":8,"tags":71},"\u002Fposts\u002Fmobile-debugging-today","現在怎麼 debug 手機網頁：從裝一堆工具到幾乎不用裝","對照 2022 年那套 scoop、iTunes、remotedebug-ios-webkit-adapter 的繁瑣設定，整理現在偵錯手機網頁的做法：Android 無線偵錯、iOS 的實際選項、on-device console、Playwright 裝置模擬，以及本機 HTTPS 的處理。",[72,73,74,75,76],"debug","iOS","Android","Chrome DevTools","Playwright",{"path":78,"title":79,"description":80,"category":16,"tags":81},"\u002Fposts\u002Fplaywright-vs-cypress","E2E 測試現在選什麼：Playwright 與 Cypress 的實際差異","對照幾年前用 Cypress 的做法，說明 Playwright 為什麼變成多數新專案的預設選擇：真正的多瀏覽器支援、免費的平行執行、trace viewer 除錯，以及兩者在架構上的根本差異與各自仍然適合的場景。",[76,82,83,84],"Cypress","E2E","測試",{"path":86,"title":87,"description":88,"category":89,"tags":90},"\u002Fposts\u002Fjs-encoding-today","現在處理編碼：TextEncoder、Uint8Array 的 Base64 方法與 URLSearchParams","整理 JavaScript 現代編碼方式，使用 TextEncoder、TextDecoder 與 Uint8Array 處理 UTF-8 與 Base64，並說明 Base64URL、URLSearchParams 與 encodeURIComponent 的差異。","research",[91,92,93,94,95,18],"編碼","TextEncoder","Base64","URLSearchParams","Uint8Array",{"path":97,"title":98,"description":99,"category":16,"tags":100},"\u002Fposts\u002Fdart-sass-migration","Dart Sass 遷移筆記：node-sass 之後怎麼裝 Sass","node-sass 與 LibSass 已停止維護，現在改用 Dart Sass。整理 webpack 與 Vite 的安裝方式，以及為什麼不再像 node-sass 一樣容易遇到 Node.js 版本與原生編譯問題。",[101,102,103],"Sass","Webpack","Vite",{"path":105,"title":106,"description":107,"category":16,"tags":108},"\u002Fposts\u002Fdayjs-vue3-computed","day.js 搭配 Vue 3 的日期格式化","整理 Vue 3 日期格式化的常見做法，使用 day.js 取代舊專案常見的 moment.js，並以 method 或 computed 取代 Vue 2 filter，也補充原生 Intl.DateTimeFormat 的使用方式。",[44,109,110,111],"day.js","日期","Intl",{"path":113,"title":114,"description":115,"category":16,"tags":116},"\u002Fposts\u002Fes-modules-today","ES Module 現在的樣子：Node ESM、exports 與 import maps","整理現代 JavaScript ES Module 的常見用法，包括 Node.js 原生 ESM、package.json exports、瀏覽器 import maps、JSON import attributes，以及 Vite 動態載入的注意事項。",[117,118,119,120,103,18],"ES Module","Node.js","import maps","exports",{"path":122,"title":123,"description":124,"category":89,"tags":125},"\u002Fposts\u002Fstructured-clone","structuredClone()：終於有內建的深層拷貝","整理 JavaScript structuredClone() 的深層拷貝用法，說明 Date、Map、Set、循環參考的處理方式，以及 Function、class 與 Vue reactive Proxy 的注意事項。",[18,126,127,128],"structuredClone","深拷貝","Vue",{"path":130,"title":131,"description":132,"category":16,"tags":133},"\u002Fposts\u002Fdynamic-import","JavaScript 動態載入：import() 與 Vue 非同步元件","整理靜態 import、動態 import() 與 Vue defineAsyncComponent 的使用方式，並簡單補充 Vite import.meta.glob 的用途。",[18,134,135,103,136],"ES Modules","Vue 3","Code Splitting",{"path":138,"title":139,"description":140,"category":16,"tags":141},"\u002Fposts\u002Fscreen-wake-lock-api","Screen Wake Lock API：不用套件也能讓頁面保持亮著","整理 Screen Wake Lock API 的基本用法，說明 HTTPS、頁面可見性、切換分頁後重新取得 wake lock，以及 Vue／Nuxt 計時器使用時的注意事項。",[142,128,143,144,145,146],"Wake Lock","Nuxt4","行動裝置","計時器","Web API",{"path":148,"title":149,"description":150,"category":16,"tags":151},"\u002Fposts\u002Fhtml-meta-boilerplate-today","現在的 HTML meta 標籤怎麼寫","整理現在一個新頁面該有的 meta 標籤骨架——viewport 補上 viewport-fit=cover 因應瀏海與圓角螢幕、color-scheme 讓表單控制項跟著深色模式換色、theme-color 決定行動瀏覽器工具列顏色，以及基本的 SEO 與 Open Graph 標籤。",[152,153,154],"HTML","meta","SEO",{"path":156,"title":157,"description":158,"category":8,"tags":159},"\u002Fposts\u002Fdocker-node-postgres","把 Node + PostgreSQL 的 backend 裝進 Docker：Dockerfile、.dockerignore 與 compose","從「本機裝了就跑」到打包成容器的通用做法——Dockerfile 每一行的理由與 layer cache 為什麼決定了指令順序、.dockerignore 漏掉 node_modules 對原生模組是災難級的問題、容器之間為什麼不能用 localhost 互相找到對方，以及怎麼驗證資料真的寫進了 volume。",[160,161,118,162,163],"Docker","Docker Compose","PostgreSQL","Express",{"path":165,"title":166,"description":167,"category":16,"tags":168},"\u002Fposts\u002Fnuxt-server-proxy","Nuxt 3 \u002F 4 用 Server API 做代理與轉發","整理 Nuxt 3 與 Nuxt 4 使用 server\u002Fapi 轉發第三方 API 的方式，可以避免瀏覽器 CORS 問題、隱藏 API Key，也能在伺服器端加入快取。",[169,143,170,171,172],"Nuxt3","CORS","Nitro","API",{"path":174,"title":175,"description":176,"category":16,"tags":177},"\u002Fposts\u002Fnuxt4-project-setup","Nuxt 4 建立專案：和 Nuxt 3 有哪些不同","整理 Nuxt 4 建立專案的基本流程與目錄結構，並對照 Nuxt 3 的差異，包括 app\u002F 目錄、路徑別名、TypeScript，以及目前常用的 ESLint 與 Tailwind CSS 4 設定。",[143,44,178,179,180],"TypeScript","ESLint","Tailwind CSS 4",{"path":182,"title":183,"description":184,"category":16,"tags":185},"\u002Fposts\u002Fnuxt3-route-middleware","Nuxt 3 \u002F 4 的 Route Middleware：進頁面前先做判斷","整理 Nuxt 3 與 Nuxt 4 的 Route Middleware，用 defineNuxtRouteMiddleware 在進入頁面前檢查登入狀態，搭配 navigateTo 重新導向，並說明一般 middleware、global middleware 與 definePageMeta 的用法。",[169,143,186,187],"Middleware","驗證",{"path":189,"title":190,"description":191,"category":16,"tags":192},"\u002Fposts\u002Fnuxt-route-rules-redirect","Nuxt 3 \u002F 4 的 routeRules：重新導向與頁面設定","整理 Nuxt 3 與 Nuxt 4 的 routeRules 用法，可以依照不同網址設定重新導向、預渲染、SSR 與快取，並簡單對照 Nuxt 2 的舊做法。",[169,143,193,194],"routeRules","重新導向",{"path":196,"title":197,"description":198,"category":16,"tags":199},"\u002Fposts\u002Fios-iframe-google-login-blank-page","iOS iframe 串接 Google 登入出現空白頁：Popup 與頁面回收問題排查紀錄","記錄 iframe 內串接 Google 第三方登入時，部分 iOS 裝置因背景頁面遭到回收並重新載入，導致 Popup 登入流程中斷，以及最後透過 Redirect 分流與登入狀態接回機制處理的過程。",[73,200,201,202,203,204,205,206],"Safari","iframe","Google Login","GIS","OAuth","Popup","Redirect",{"path":208,"title":209,"description":210,"category":16,"tags":211},"\u002Fposts\u002Fios-login-tab-return-behavior","iOS 第三方登入後回不到原頁？分頁開啟關係與回頁行為實測","記錄 iOS Safari 與 Chrome 在多分頁第三方登入流程中，登入頁關閉後不一定回到原頁的實測，以及分頁開啟關係、window.close 與 window.focus 的限制。",[73,200,212,202,204,201,213,214],"Chrome","window.close","window.focus",{"path":216,"title":217,"description":218,"category":16,"tags":219},"\u002Fposts\u002Fgoogle-app-about-blank-placeholder","Google App 內建瀏覽器：about:blank 佔位頁被關閉的排查紀錄","記錄 iOS Google App 內建瀏覽器在非同步開啟外部頁面時，about:blank 佔位分頁被自動關閉，導致第三方登入或外部導轉失敗的排查與解法。",[73,220,221,222,204,223],"Google App","window.open","about:blank","WebView",{"path":225,"title":226,"description":227,"category":228,"tags":229},"\u002Fposts\u002Fclaude-code-codex-cli-init-basics","AI 相關紀錄 v1｜Claude Code \u002F Codex CLI 初始化與基本概念","從 Claude Code 與 Codex CLI 開始，整理 CLI、Coding Agent、Context、\u002Finit 與 CLAUDE.md／AGENTS.md 這幾個一開始最容易搞混的概念，以及在既有專案中的安裝與啟動方式。","ai",[230,231,232,233,234,235],"AI","Claude Code","Codex CLI","Coding Agent","CLAUDE.md","AGENTS.md",{"path":237,"title":238,"description":239,"category":228,"tags":240},"\u002Fposts\u002Fclaude-agents-md-project-guidelines","AI 相關紀錄 v2｜CLAUDE.md \u002F AGENTS.md 怎麼寫？專案規範與實際範例","整理 CLAUDE.md 與 AGENTS.md 的用途、適合記錄的專案規範，以及可直接參考的設定範例。",[230,231,232,234,235],{"path":242,"title":243,"description":244,"category":228,"tags":245},"\u002Fposts\u002Fclaude-codex-config-locations","AI 相關紀錄 v3｜Claude Code `.claude\u002F` \u002F Codex CLI 設定位置：全域與專案設定基本概要介紹","整理 Claude Code 與 Codex CLI 的使用者層級、專案層級設定位置，以及 rules、skills、agents 等目錄的用途。",[230,231,232,246],"設定檔",{"path":248,"title":249,"description":250,"category":228,"tags":251},"\u002Fposts\u002Fclaude-settings-codex-config","AI 相關紀錄 v4｜Claude Code settings \u002F Codex config 基本設定","整理 Claude Code settings.json 與 Codex config.toml 的設定層級、權限控制和常見基本設定。",[230,231,232,252,253],"settings.json","config.toml",{"path":255,"title":256,"description":257,"category":228,"tags":258},"\u002Fposts\u002Fclaude-codex-rules-structure","AI 相關紀錄 v5｜Rules：專案規則多了之後怎麼拆？","整理 Claude Code Rules 與 Codex AGENTS.md 的拆分方式，說明何時該拆、怎麼依目錄管理專案規則。",[230,231,232,259,235],"Rules",{"path":261,"title":262,"description":263,"category":228,"tags":264},"\u002Fposts\u002Fclaude-codex-slash-commands","AI 相關紀錄 v6｜Commands \u002F Slash Commands：CLI 裡的 `\u002F` 指令是什麼？","介紹 Claude Code 與 Codex CLI 的 Slash Commands，整理常用內建指令、自訂指令與 Skills 的差異。",[230,231,232,265],"Slash Commands",{"path":267,"title":268,"description":269,"category":228,"tags":270},"\u002Fposts\u002Fclaude-codex-skills","AI 相關紀錄 v7｜Skills：把重複工作流程整理起來","介紹 Claude Code 與 Codex Skills，整理適合保存成 Skill 的重複流程、基本結構與使用方式。",[230,231,232,271],"Skills",{"path":273,"title":274,"description":275,"category":228,"tags":276},"\u002Fposts\u002Fclaude-codex-agents-subagents","AI 相關紀錄 v8｜Agent \u002F Subagent：什麼時候真的需要分工？","整理 Agent、Subagent 與 Custom Agent 的用途，說明何時適合分工，以及它們和 Rules、Skills 的差異。",[230,231,232,277,278],"Agent","Subagent",{"path":280,"title":281,"description":282,"category":228,"tags":283},"\u002Fposts\u002Fmcp-basics","AI 相關紀錄 v9｜MCP：讓 AI 使用其他工具與資料","介紹 MCP 如何讓 AI 使用外部工具與資料，並以 Pencil MCP 說明實際用途、權限風險及與 Skill 的差異。",[230,284,231,232,285],"MCP","Pencil",{"path":287,"title":288,"description":289,"category":228,"tags":290},"\u002Fposts\u002Fai-coding-agent-concepts-summary","AI 相關紀錄 v10｜基本概念小結論","用一篇快速整理 CLAUDE.md、AGENTS.md、設定檔、Rules、Commands、Skills、Agent 與 MCP 的角色和差異。",[230,231,232,284,271,277],{"path":292,"title":293,"description":294,"category":228,"tags":295},"\u002Fposts\u002Fclaude-code-codex-model-selection","Claude Code 與 Codex：不同模型怎麼選？","比較 Claude Code 與 Codex 不同模型的速度、能力與成本取捨，依工作難度整理實用的選擇方向。",[230,231,296,297],"Codex","模型選擇",{"path":299,"title":300,"description":301,"category":228,"tags":302},"\u002Fposts\u002Fclaude-model-selection","Claude 模型簡單怎麼選？","整理 Claude 各模型在速度、能力與使用情境上的差異，提供從輕量工作到複雜任務的選擇方向。",[230,231,303,297],"Claude",{"path":305,"title":306,"description":307,"category":228,"tags":308},"\u002Fposts\u002Fcodex-model-selection","Codex 模型簡單怎麼選？","整理 Codex 各模型在速度、能力與使用情境上的差異，提供從簡單修改到大型端到端任務的選擇方向。",[230,296,309,297],"OpenAI",{"path":311,"title":312,"description":313,"category":228,"tags":314},"\u002Fposts\u002Fclaude-code-codex-cli-command-cheatsheet","Claude Code \u002F Codex CLI 常用指令速查表","整理 Claude Code 與 Codex CLI 的常用斜線指令、快捷操作與進階功能，方便需要時快速查找。",[230,231,232,265,315],"速查表",{"id":317,"title":149,"body":318,"category":16,"description":150,"draft":1144,"extension":1145,"image":1146,"meta":1147,"navigation":1148,"path":148,"publishedAt":1149,"relatedExperiments":1150,"seo":1151,"stem":1152,"tags":1153,"updatedAt":1149,"__hash__":1154},"posts\u002Fposts\u002F20260519.html-meta-boilerplate-today.md",{"type":319,"value":320,"toc":1133},"minimark",[321,330,334,587,591,610,628,632,657,675,704,708,733,749,753,778,785,846,850,895,905,909,1002,1005,1008,1092,1095,1129],[322,323,324,325,329],"p",{},"早期 HTML5 頁面骨架裡的 charset 跟 viewport 到現在都還適用，",[326,327,328],"code",{},"X-UA-Compatible"," 則已經沒有意義。現在一個正式頁面通常還會再多幾行。",[331,332,333],"h2",{"id":333},"基本骨架",[335,336,341],"pre",{"className":337,"code":338,"language":339,"meta":340,"style":340},"language-html shiki shiki-themes github-light github-dark-default","\u003C!DOCTYPE html>\n\u003Chtml lang=\"zh-Hant\">\n\u003Chead>\n  \u003Cmeta charset=\"UTF-8\">\n  \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, viewport-fit=cover\">\n  \u003Cmeta name=\"color-scheme\" content=\"light dark\">\n  \u003Cmeta name=\"theme-color\" content=\"#000000\">\n  \u003Ctitle>頁面標題\u003C\u002Ftitle>\n  \u003Cmeta name=\"description\" content=\"這個頁面在做什麼，一到兩句話\">\n  \u003Clink rel=\"canonical\" href=\"https:\u002F\u002Fexample.com\u002Fcurrent-page\">\n  \u003Clink rel=\"icon\" href=\"\u002Ffavicon.svg\" type=\"image\u002Fsvg+xml\">\n\u003C\u002Fhead>\n","html","",[326,342,343,363,383,393,411,436,459,482,497,520,546,577],{"__ignoreMap":340},[344,345,348,352,356,360],"span",{"class":346,"line":347},"line",1,[344,349,351],{"class":350},"szmH1","\u003C!",[344,353,355],{"class":354},"sXQ1o","DOCTYPE",[344,357,359],{"class":358},"syv4q"," html",[344,361,362],{"class":350},">\n",[344,364,366,369,371,374,377,381],{"class":346,"line":365},2,[344,367,368],{"class":350},"\u003C",[344,370,339],{"class":354},[344,372,373],{"class":358}," lang",[344,375,376],{"class":350},"=",[344,378,380],{"class":379},"sNbzA","\"zh-Hant\"",[344,382,362],{"class":350},[344,384,386,388,391],{"class":346,"line":385},3,[344,387,368],{"class":350},[344,389,390],{"class":354},"head",[344,392,362],{"class":350},[344,394,396,399,401,404,406,409],{"class":346,"line":395},4,[344,397,398],{"class":350},"  \u003C",[344,400,153],{"class":354},[344,402,403],{"class":358}," charset",[344,405,376],{"class":350},[344,407,408],{"class":379},"\"UTF-8\"",[344,410,362],{"class":350},[344,412,414,416,418,421,423,426,429,431,434],{"class":346,"line":413},5,[344,415,398],{"class":350},[344,417,153],{"class":354},[344,419,420],{"class":358}," name",[344,422,376],{"class":350},[344,424,425],{"class":379},"\"viewport\"",[344,427,428],{"class":358}," content",[344,430,376],{"class":350},[344,432,433],{"class":379},"\"width=device-width, initial-scale=1.0, viewport-fit=cover\"",[344,435,362],{"class":350},[344,437,439,441,443,445,447,450,452,454,457],{"class":346,"line":438},6,[344,440,398],{"class":350},[344,442,153],{"class":354},[344,444,420],{"class":358},[344,446,376],{"class":350},[344,448,449],{"class":379},"\"color-scheme\"",[344,451,428],{"class":358},[344,453,376],{"class":350},[344,455,456],{"class":379},"\"light dark\"",[344,458,362],{"class":350},[344,460,462,464,466,468,470,473,475,477,480],{"class":346,"line":461},7,[344,463,398],{"class":350},[344,465,153],{"class":354},[344,467,420],{"class":358},[344,469,376],{"class":350},[344,471,472],{"class":379},"\"theme-color\"",[344,474,428],{"class":358},[344,476,376],{"class":350},[344,478,479],{"class":379},"\"#000000\"",[344,481,362],{"class":350},[344,483,485,487,490,493,495],{"class":346,"line":484},8,[344,486,398],{"class":350},[344,488,489],{"class":354},"title",[344,491,492],{"class":350},">頁面標題\u003C\u002F",[344,494,489],{"class":354},[344,496,362],{"class":350},[344,498,500,502,504,506,508,511,513,515,518],{"class":346,"line":499},9,[344,501,398],{"class":350},[344,503,153],{"class":354},[344,505,420],{"class":358},[344,507,376],{"class":350},[344,509,510],{"class":379},"\"description\"",[344,512,428],{"class":358},[344,514,376],{"class":350},[344,516,517],{"class":379},"\"這個頁面在做什麼，一到兩句話\"",[344,519,362],{"class":350},[344,521,523,525,528,531,533,536,539,541,544],{"class":346,"line":522},10,[344,524,398],{"class":350},[344,526,527],{"class":354},"link",[344,529,530],{"class":358}," rel",[344,532,376],{"class":350},[344,534,535],{"class":379},"\"canonical\"",[344,537,538],{"class":358}," href",[344,540,376],{"class":350},[344,542,543],{"class":379},"\"https:\u002F\u002Fexample.com\u002Fcurrent-page\"",[344,545,362],{"class":350},[344,547,549,551,553,555,557,560,562,564,567,570,572,575],{"class":346,"line":548},11,[344,550,398],{"class":350},[344,552,527],{"class":354},[344,554,530],{"class":358},[344,556,376],{"class":350},[344,558,559],{"class":379},"\"icon\"",[344,561,538],{"class":358},[344,563,376],{"class":350},[344,565,566],{"class":379},"\"\u002Ffavicon.svg\"",[344,568,569],{"class":358}," type",[344,571,376],{"class":350},[344,573,574],{"class":379},"\"image\u002Fsvg+xml\"",[344,576,362],{"class":350},[344,578,580,583,585],{"class":346,"line":579},12,[344,581,582],{"class":350},"\u003C\u002F",[344,584,390],{"class":354},[344,586,362],{"class":350},[331,588,590],{"id":589},"lang-屬性不要漏掉","lang 屬性：不要漏掉",[335,592,594],{"className":337,"code":593,"language":339,"meta":340,"style":340},"\u003Chtml lang=\"zh-Hant\">\n",[326,595,596],{"__ignoreMap":340},[344,597,598,600,602,604,606,608],{"class":346,"line":347},[344,599,368],{"class":350},[344,601,339],{"class":354},[344,603,373],{"class":358},[344,605,376],{"class":350},[344,607,380],{"class":379},[344,609,362],{"class":350},[322,611,612,615,616,619,620,623,624,627],{},[326,613,614],{},"lang"," 不是新東西，但很常被漏掉。它告訴螢幕閱讀器該用哪種語言的發音規則朗讀內容，也讓瀏覽器的翻譯功能、拼字檢查抓對語言。中文頁面常見的寫法是 ",[326,617,618],{},"zh-Hant","（繁體）或 ",[326,621,622],{},"zh-Hans","（簡體），不要只寫籠統的 ",[326,625,626],{},"zh","。",[331,629,631],{"id":630},"viewport多了-viewport-fitcover","viewport：多了 viewport-fit=cover",[335,633,635],{"className":337,"code":634,"language":339,"meta":340,"style":340},"\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, viewport-fit=cover\">\n",[326,636,637],{"__ignoreMap":340},[344,638,639,641,643,645,647,649,651,653,655],{"class":346,"line":347},[344,640,368],{"class":350},[344,642,153],{"class":354},[344,644,420],{"class":358},[344,646,376],{"class":350},[344,648,425],{"class":379},[344,650,428],{"class":358},[344,652,376],{"class":350},[344,654,433],{"class":379},[344,656,362],{"class":350},[322,658,659,662,663,666,667,670,671,674],{},[326,660,661],{},"width=device-width, initial-scale=1.0"," 這兩個值跟以前一樣沒變。新加的 ",[326,664,665],{},"viewport-fit=cover"," 是因應瀏海、藥丸挖孔、圓角螢幕這類「不是傳統矩形」的手機螢幕——沒有這個設定，頁面內容會自動避開瀏海周圍的安全區域，畫面兩側或底部可能留下不必要的黑邊。加上 ",[326,668,669],{},"cover"," 之後，頁面可以延伸到整個螢幕，但你要自己用 CSS 的 ",[326,672,673],{},"env(safe-area-inset-*)"," 補回安全邊界：",[335,676,680],{"className":677,"code":678,"language":679,"meta":340,"style":340},"language-css shiki shiki-themes github-light github-dark-default",".bottom-nav {\n  padding-bottom: env(safe-area-inset-bottom);\n}\n","css",[326,681,682,690,699],{"__ignoreMap":340},[344,683,684,687],{"class":346,"line":347},[344,685,686],{"class":358},".bottom-nav",[344,688,689],{"class":350}," {\n",[344,691,692,696],{"class":346,"line":365},[344,693,695],{"class":694},"sPgVT","  padding-bottom",[344,697,698],{"class":350},": env(safe-area-inset-bottom);\n",[344,700,701],{"class":346,"line":385},[344,702,703],{"class":350},"}\n",[331,705,707],{"id":706},"color-scheme讓原生控制項跟著換色","color-scheme：讓原生控制項跟著換色",[335,709,711],{"className":337,"code":710,"language":339,"meta":340,"style":340},"\u003Cmeta name=\"color-scheme\" content=\"light dark\">\n",[326,712,713],{"__ignoreMap":340},[344,714,715,717,719,721,723,725,727,729,731],{"class":346,"line":347},[344,716,368],{"class":350},[344,718,153],{"class":354},[344,720,420],{"class":358},[344,722,376],{"class":350},[344,724,449],{"class":379},[344,726,428],{"class":358},[344,728,376],{"class":350},[344,730,456],{"class":379},[344,732,362],{"class":350},[322,734,735,736,739,740,743,744,748],{},"沒有這行，就算你的 CSS 已經做好深色模式，捲軸、",[326,737,738],{},"\u003Cselect>"," 下拉選單、",[326,741,742],{},"\u003Cinput type=\"date\">"," 這類",[745,746,747],"strong",{},"瀏覽器原生渲染的控制項","，在深色頁面上還是可能保持刺眼的白色底。加上這行，等於告訴瀏覽器「這個頁面支援淺色跟深色兩種模式」，原生控制項會依系統或頁面目前的模式自動換色。",[331,750,752],{"id":751},"theme-color行動瀏覽器工具列顏色","theme-color：行動瀏覽器工具列顏色",[335,754,756],{"className":337,"code":755,"language":339,"meta":340,"style":340},"\u003Cmeta name=\"theme-color\" content=\"#000000\">\n",[326,757,758],{"__ignoreMap":340},[344,759,760,762,764,766,768,770,772,774,776],{"class":346,"line":347},[344,761,368],{"class":350},[344,763,153],{"class":354},[344,765,420],{"class":358},[344,767,376],{"class":350},[344,769,472],{"class":379},[344,771,428],{"class":358},[344,773,376],{"class":350},[344,775,479],{"class":379},[344,777,362],{"class":350},[322,779,780,781,784],{},"決定手機瀏覽器網址列、狀態列背景的顏色，讓網頁跟系統介面融合在一起。深淺色模式想給不同顏色，可以放兩個標籤，用 ",[326,782,783],{},"media"," 屬性區分：",[335,786,788],{"className":337,"code":787,"language":339,"meta":340,"style":340},"\u003Cmeta name=\"theme-color\" content=\"#ffffff\" media=\"(prefers-color-scheme: light)\">\n\u003Cmeta name=\"theme-color\" content=\"#000000\" media=\"(prefers-color-scheme: dark)\">\n",[326,789,790,819],{"__ignoreMap":340},[344,791,792,794,796,798,800,802,804,806,809,812,814,817],{"class":346,"line":347},[344,793,368],{"class":350},[344,795,153],{"class":354},[344,797,420],{"class":358},[344,799,376],{"class":350},[344,801,472],{"class":379},[344,803,428],{"class":358},[344,805,376],{"class":350},[344,807,808],{"class":379},"\"#ffffff\"",[344,810,811],{"class":358}," media",[344,813,376],{"class":350},[344,815,816],{"class":379},"\"(prefers-color-scheme: light)\"",[344,818,362],{"class":350},[344,820,821,823,825,827,829,831,833,835,837,839,841,844],{"class":346,"line":365},[344,822,368],{"class":350},[344,824,153],{"class":354},[344,826,420],{"class":358},[344,828,376],{"class":350},[344,830,472],{"class":379},[344,832,428],{"class":358},[344,834,376],{"class":350},[344,836,479],{"class":379},[344,838,811],{"class":358},[344,840,376],{"class":350},[344,842,843],{"class":379},"\"(prefers-color-scheme: dark)\"",[344,845,362],{"class":350},[331,847,849],{"id":848},"基本-seodescription-與-canonical","基本 SEO：description 與 canonical",[335,851,853],{"className":337,"code":852,"language":339,"meta":340,"style":340},"\u003Cmeta name=\"description\" content=\"這個頁面在做什麼，一到兩句話\">\n\u003Clink rel=\"canonical\" href=\"https:\u002F\u002Fexample.com\u002Fcurrent-page\">\n",[326,854,855,875],{"__ignoreMap":340},[344,856,857,859,861,863,865,867,869,871,873],{"class":346,"line":347},[344,858,368],{"class":350},[344,860,153],{"class":354},[344,862,420],{"class":358},[344,864,376],{"class":350},[344,866,510],{"class":379},[344,868,428],{"class":358},[344,870,376],{"class":350},[344,872,517],{"class":379},[344,874,362],{"class":350},[344,876,877,879,881,883,885,887,889,891,893],{"class":346,"line":365},[344,878,368],{"class":350},[344,880,527],{"class":354},[344,882,530],{"class":358},[344,884,376],{"class":350},[344,886,535],{"class":379},[344,888,538],{"class":358},[344,890,376],{"class":350},[344,892,543],{"class":379},[344,894,362],{"class":350},[322,896,897,900,901,904],{},[326,898,899],{},"description"," 常被搜尋引擎直接拿來當搜尋結果的摘要文字，寫清楚一點比讓 Google 自己從內文亂抓一段划算。",[326,902,903],{},"canonical"," 在同一份內容有多個網址能存取時（例如帶不帶查詢字串、有沒有結尾斜線），告訴搜尋引擎哪一個才是「正式」的網址，避免內容被判定為重複而分散排名。",[331,906,908],{"id":907},"要分享到社群時再加open-graph","要分享到社群時再加：Open Graph",[335,910,912],{"className":337,"code":911,"language":339,"meta":340,"style":340},"\u003Cmeta property=\"og:title\" content=\"頁面標題\">\n\u003Cmeta property=\"og:description\" content=\"頁面描述\">\n\u003Cmeta property=\"og:image\" content=\"https:\u002F\u002Fexample.com\u002Fog-image.png\">\n\u003Cmeta property=\"og:url\" content=\"https:\u002F\u002Fexample.com\u002Fcurrent-page\">\n",[326,913,914,937,959,981],{"__ignoreMap":340},[344,915,916,918,920,923,925,928,930,932,935],{"class":346,"line":347},[344,917,368],{"class":350},[344,919,153],{"class":354},[344,921,922],{"class":358}," property",[344,924,376],{"class":350},[344,926,927],{"class":379},"\"og:title\"",[344,929,428],{"class":358},[344,931,376],{"class":350},[344,933,934],{"class":379},"\"頁面標題\"",[344,936,362],{"class":350},[344,938,939,941,943,945,947,950,952,954,957],{"class":346,"line":365},[344,940,368],{"class":350},[344,942,153],{"class":354},[344,944,922],{"class":358},[344,946,376],{"class":350},[344,948,949],{"class":379},"\"og:description\"",[344,951,428],{"class":358},[344,953,376],{"class":350},[344,955,956],{"class":379},"\"頁面描述\"",[344,958,362],{"class":350},[344,960,961,963,965,967,969,972,974,976,979],{"class":346,"line":385},[344,962,368],{"class":350},[344,964,153],{"class":354},[344,966,922],{"class":358},[344,968,376],{"class":350},[344,970,971],{"class":379},"\"og:image\"",[344,973,428],{"class":358},[344,975,376],{"class":350},[344,977,978],{"class":379},"\"https:\u002F\u002Fexample.com\u002Fog-image.png\"",[344,980,362],{"class":350},[344,982,983,985,987,989,991,994,996,998,1000],{"class":346,"line":395},[344,984,368],{"class":350},[344,986,153],{"class":354},[344,988,922],{"class":358},[344,990,376],{"class":350},[344,992,993],{"class":379},"\"og:url\"",[344,995,428],{"class":358},[344,997,376],{"class":350},[344,999,543],{"class":379},[344,1001,362],{"class":350},[322,1003,1004],{},"網址被貼到 Facebook、LINE、Slack 這類支援 Open Graph 的平台時，會依這幾個標籤產生預覽卡片（標題、描述、縮圖）。沒有設定時，平台通常會退而求其次亂抓頁面內容，效果不可控，正式上線的頁面值得把這幾行補齊。",[331,1006,1007],{"id":1007},"對照表",[1009,1010,1011,1026],"table",{},[1012,1013,1014],"thead",{},[1015,1016,1017,1020,1023],"tr",{},[1018,1019],"th",{},[1018,1021,1022],{},"2020 年常見寫法",[1018,1024,1025],{},"現在",[1027,1028,1029,1044,1056,1069,1082],"tbody",{},[1015,1030,1031,1035,1039],{},[1032,1033,1034],"td",{},"viewport",[1032,1036,1037],{},[326,1038,661],{},[1032,1040,1041,1042],{},"多了 ",[326,1043,665],{},[1015,1045,1046,1049,1053],{},[1032,1047,1048],{},"IE 相容性",[1032,1050,1051],{},[326,1052,328],{},[1032,1054,1055],{},"已無意義，可移除",[1015,1057,1058,1061,1064],{},[1032,1059,1060],{},"深色模式",[1032,1062,1063],{},"沒有標準做法",[1032,1065,1066],{},[326,1067,1068],{},"color-scheme",[1015,1070,1071,1074,1077],{},[1032,1072,1073],{},"行動瀏覽器介面色",[1032,1075,1076],{},"沒有",[1032,1078,1079],{},[326,1080,1081],{},"theme-color",[1015,1083,1084,1087,1089],{},[1032,1085,1086],{},"社群分享預覽",[1032,1088,1076],{},[1032,1090,1091],{},"Open Graph 標籤",[331,1093,1094],{"id":1094},"參考資料",[1096,1097,1098,1108,1115,1122],"ul",{},[1099,1100,1101],"li",{},[1102,1103,1107],"a",{"href":1104,"rel":1105},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-TW\u002Fdocs\u002FWeb\u002FHTML\u002FElement\u002Fmeta",[1106],"nofollow","MDN：meta 元素",[1099,1109,1110],{},[1102,1111,1114],{"href":1112,"rel":1113},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTML\u002FElement\u002Fmeta\u002Fname\u002Fcolor-scheme",[1106],"MDN：color-scheme",[1099,1116,1117],{},[1102,1118,1121],{"href":1119,"rel":1120},"https:\u002F\u002Fogp.me\u002F",[1106],"The Open Graph protocol",[1099,1123,1124],{},[1102,1125,1128],{"href":1126,"rel":1127},"https:\u002F\u002Fweb.dev\u002Farticles\u002Fviewport",[1106],"web.dev：Viewport 與安全區域",[1130,1131,1132],"style",{},"html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}",{"title":340,"searchDepth":385,"depth":385,"links":1134},[1135,1136,1137,1138,1139,1140,1141,1142,1143],{"id":333,"depth":365,"text":333},{"id":589,"depth":365,"text":590},{"id":630,"depth":365,"text":631},{"id":706,"depth":365,"text":707},{"id":751,"depth":365,"text":752},{"id":848,"depth":365,"text":849},{"id":907,"depth":365,"text":908},{"id":1007,"depth":365,"text":1007},{"id":1094,"depth":365,"text":1094},false,"md",null,{},true,"2026-05-19",[],{"title":149,"description":150},"posts\u002F20260519.html-meta-boilerplate-today",[152,153,154],"NLoS9aluzL6ATnQeH1TwtEm4PQkWiPNyKL9AKCd-Yrg",[1156,1159],{"title":157,"path":156,"stem":1157,"description":158,"category":8,"publishedAt":1158,"children":-1},"posts\u002F20260613.docker-node-postgres","2026-06-13",{"title":131,"path":130,"stem":1160,"description":132,"category":16,"publishedAt":1161,"children":-1},"posts\u002F20260417.dynamic-import","2026-04-17",1791786901167]