Side Projects

個人技術網站

以 Next.js 15、TypeScript 與 Tailwind CSS 打造,支援中英雙語、深淺色主題與三層 design token 架構,全程以 AI-augmented 工作流開發與維護。

要解決的問題

我需要一個地方,把工作經歷、專案與平常做的小工具整理在一起,讓招募方、合作對象和中英文讀者都能快速看懂我在做什麼。履歷 PDF 和社群個人頁放不下專案細節,也很難被搜尋引擎找到。這個網站就是那個集中、可以持續更新的入口,也是我實際練習 AI 協作開發流程的專案。

主要功能

  • 中英雙語切換

    所有頁面提供繁體中文與英文,第一次造訪依瀏覽器語言決定,之後記住使用者的選擇。

  • 深色與淺色主題

    預設為深色,可隨時切換;兩種主題都是各自調整過的配色,而不是單純反轉顏色。

  • 每個專案都有獨立頁面

    作品集裡的每個專案都在建置時產生靜態頁面,附上各自的標題、描述、canonical 網址、Open Graph 圖片與結構化資料,並收錄在 sitemap 中。

  • 工具頁

    集中列出我做的網頁小工具,例如學測落點分析、大老二計分板、德州撲克 Home Game 工具、麻將算台器與塔防遊戲。

  • 自動產生的分享圖

    社群分享用的 Open Graph 與 Twitter 圖片由程式在 edge 執行時產生,不需要另外維護圖檔。

技術架構

網站使用 Next.js 15 App Router 與 React 19,以 TypeScript 撰寫,部署在 Vercel。樣式是 Tailwind CSS 3,元件以 shadcn/ui(Radix primitives)為基礎,主題切換用 next-themes。專案資料直接放在 TypeScript 檔案裡,沒有資料庫也沒有 CMS:內容量小、更新頻率低,放在程式碼裡可以享有型別檢查與版本控制,也讓每一頁都能在建置時靜態產生。SEO 相關的 metadata、JSON-LD 與 sitemap 都由同一份專案資料推導,新增專案時不必在多個地方重複填寫。

技術挑戰與取捨

  • 語系切換造成的 hydration 不一致

    語系存在瀏覽器的 localStorage,但伺服器在渲染時讀不到,結果伺服器輸出中文、瀏覽器卻用英文渲染,React 就會出現 hydration mismatch。曾考慮把語系放進網址路徑或 cookie,但那會牽動整個路由結構。最後用 useSyncExternalStore 管理語系:伺服器與 hydration 階段一律使用預設語系,確保兩邊輸出相同,hydration 完成後再切換成使用者實際的語系。讀取主題的元件也採同樣原則,等元件掛載後才顯示依賴主題的內容。

  • 讓每個專案頁都能被搜尋引擎好好理解

    作品集原本只是一頁卡片,專案內容很難被單獨搜尋到。改成每個專案一個靜態路由,用 generateStaticParams 在建置時產生,並以共用的 pageMetadata 函式產生標題、描述、canonical 與社群分享資訊,另外輸出 BreadcrumbList 與 CreativeWork 的 JSON-LD。過程中發現子頁面一旦覆寫 openGraph,根目錄自動產生的分享圖就不會被帶入,因此沒有專屬圖片的專案會明確沿用上層的圖片。輸出到頁面的 JSON-LD 也會跳脫 < 字元,避免內容意外關閉 script 標籤。

  • 深淺色主題的色彩管理

    早期元件直接寫 Tailwind 的色票,例如 text-cyan-400,加入淺色主題後許多文字在白底上看不清楚。重構成三層 design token:底層是原始色階,中間是依用途命名的語意 token,並分別為淺色與深色定義,最上層才是 Tailwind class,元件只引用語意 token。其中刻意區分「填色用的主色」與「文字用的品牌色」,因為適合當底色的青色不一定適合當白底上的文字。規則與例外情況寫在 design system 文件中,並附上一行檢查指令找出殘留的原始色票。

現況

公開網站,網址為 www.jjlindev.com,部署在 Vercel,持續更新中。

使用技術

  • Next.js 15
  • TypeScript
  • Tailwind CSS
  • Vercel

更多Side Projects