為 Astro 部落格打造互動式 About 頁面:React + Tailwind 組件實戰

6857 字
18 min
2/17/2026
為 Astro 部落格打造互動式 About 頁面:React + Tailwind 組件實戰

簡單來說

記錄如何在 Astro + MDX 專案中整合 React 組件,打造包含社群連結、技能標籤和時間軸的精美 About 頁面,以及在白色背景下優化配色的實戰經驗。

TL;DR

前言

一個好的個人網站少不了一個精心設計的「關於我」頁面。今天的任務是為部落格的 About 頁面創建三個互動式 React 組件:

  1. SocialLinks - 社群媒體連結卡片
  2. TechStack - 技能標籤展示
  3. Timeline - 職涯成長時間軸

這些組件不僅要好看,還要在白色背景下有良好的可讀性和對比度。


技術選型與架構

技術棧

  • Astro - 靜態站點生成器,支援 Islands Architecture
  • React - 用於構建互動組件
  • MDX - 在 Markdown 中嵌入 React 組件
  • Tailwind CSS - 快速樣式開發
  • lucide-react - 現代化的圖標庫

為什麼這樣選?

Astro 的 Islands Architecture 讓我可以只在需要的地方注入 JavaScript,
其他部分保持靜態 HTML,兼顧互動性與效能。

開發過程

第一步:安裝依賴

pnpm add lucide-react

lucide-react 是輕量級的圖標庫,比傳統的 Font Awesome 更適合現代化專案。

第二步:組件文件結構

根據 MDX 的 import 路徑規則,組件需要放在相對路徑可訪問的位置:

 components/
 ├── SocialLinks.tsx
 ├── TechStack.tsx
 └── Timeline.tsx

組件設計與實作

設計目標:

  • 圓角矩形卡片設計
  • 每個平台有專屬 hover 配色
  • 平滑的縮放與顏色過渡動畫

核心程式碼:

import { Github, Twitter, Mail, Linkedin } from "lucide-react";

export function SocialLinks() {
  return (
    <div className="not-prose flex flex-wrap gap-3 my-6">
      {links.map(({ href, label, icon: Icon, hoverBg, hoverText }) => (
        <a
          href={href}
          className={`
            inline-flex items-center gap-2 px-4 py-2
            rounded-lg border border-gray-200
            bg-gray-50 text-gray-600
            transition-all duration-300
            ${hoverBg} ${hoverText}
            hover:shadow-md hover:scale-105
          `}
        >
          <Icon size={18} />
          <span>{label}</span>
        </a>
      ))}
    </div>
  );
}

關鍵技巧:

  • 使用 not-prose class 避免被 Tailwind Typography 影響
  • 動態 className 讓每個按鈕有不同的 hover 效果
  • transition-all duration-300 創造平滑動畫

2. TechStack - 技能標籤組件

設計目標:

  • 每個技術有專屬配色
  • Flex wrap 自動換行
  • Hover 時微縮放效果

核心程式碼:

const colorMap: Record<string, { bg: string; border: string; text: string }> = {
  React:       { bg: "bg-sky-200",    border: "border-sky-600",    text: "text-sky-900" },
  TypeScript:  { bg: "bg-blue-200",   border: "border-blue-600",   text: "text-blue-900" },
  TailwindCSS: { bg: "bg-teal-200",   border: "border-teal-600",   text: "text-teal-900" },
  // ...更多配色
};

export function TechStack({ languages }: TechStackProps) {
  return (
    <div className="not-prose flex flex-wrap gap-2.5 my-4">
      {languages.map((lang) => {
        const color = colorMap[lang] ?? defaultColor;
        return (
          <span className={`${color.bg} ${color.border} ${color.text} ...`}>
            {lang}
          </span>
        );
      })}
    </div>
  );
}

調色心得:

  • 初版使用 -50-100 的淺色,在白背景下幾乎看不見
  • 最終定案:背景使用 -200,邊框 -600,文字 -900
  • 這樣的組合在白色背景下有極佳的對比度

3. Timeline - 時間軸組件

設計目標:

  • 左側垂直線 + 圓圈節點
  • 右側內容區(年份、標題、描述)
  • Hover 時圓圈放大並發光
  • 支援彩色 badge 標籤

核心程式碼:

export function Timeline({ items }: TimelineProps) {
  return (
    <div className="not-prose relative my-8">
      {items.map((item, index) => (
        <div className="group relative flex gap-6 pb-10">
          {/* 左側時間軸 */}
          <div className="relative flex flex-col items-center">
            <div className="
              rounded-full border-2 border-blue-600 bg-white
              group-hover:scale-150 group-hover:bg-blue-600
              group-hover:shadow-[0_0_12px_rgba(37,99,235,0.6)]
            " />
            {/* 垂直連接線 */}
            <div className="absolute top-4 h-full w-0.5 bg-gray-300" />
          </div>

          {/* 右側內容 */}
          <div className="flex-1">
            <span className="text-blue-800 font-bold">{item.year}</span>
            <h3 className="text-gray-900 font-bold">{item.title}</h3>
            <p className="text-gray-800">{item.description}</p>
          </div>
        </div>
      ))}
    </div>
  );
}

互動亮點:

  • 使用 Tailwind 的 groupgroup-hover 實現整體 hover 效果
  • 圓圈節點的發光效果使用 shadow-[0_0_12px_rgba(...)] 自訂陰影
  • scale-150 讓圓圈在 hover 時放大,視覺反饋明顯

踩坑與解決方案

問題 1:MDX Import 順序錯誤

錯誤訊息:

[@mdx-js/rollup] Could not parse import/exports with acorn
Unexpected character ' '

原因: 在 MDX 中,所有 import 語句必須放在 frontmatter 之後、Markdown 內容之前。我一開始把標題寫在 import 前面了。

解決方案:

---
title: 關於我
---

import { SocialLinks } from './components/SocialLinks';
import { TechStack } from './components/TechStack';
import { Timeline } from './components/Timeline';

# 正文內容開始

問題 2:白色背景下對比度不足

遇到的狀況:

  • TechStack 的標籤顏色太淺,幾乎看不清楚
  • Timeline 的文字是 gray-600,在白底上灰濛濛的

解決歷程:

嘗試次數配色方案結果
v1bg-50, text-700❌ 太淺,看不清
v2bg-100, text-800⚠️ 有改善但還是不夠
v3bg-200, text-900✅ 完美!對比度極佳

最終方案:

// TechStack 配色
{
  bg: "bg-sky-200",      // -200 提供明顯的背景色
  border: "border-sky-600",  // -600 清晰的邊框
  text: "text-sky-900"   // -900 最深的文字顏色
}

// Timeline 文字顏色
year: "text-blue-800"
title: "text-gray-900"
description: "text-gray-800"

關鍵經驗:

在設計白色背景的 UI 時,不要害怕使用深色(-800 / -900)。 「看起來太重」通常意味著「對比度剛剛好」。


錯誤訊息:

EPERM: operation not permitted, symlink

原因: Windows 系統下建立 symlink 需要管理員權限,這是 @astrojs/vercel adapter 的已知問題。

解決方案: 這個錯誤只會出現在本地建置時,實際部署到 Vercel 時不會出現。如果真的需要在本地完整測試 Vercel 建置,可以用管理員權限執行 terminal。


在 MDX 中使用組件

完成組件後,在 about.mdx 中這樣使用:

---
title: 關於我
publishedDate: 2026-02-17
---

import { SocialLinks } from './components/SocialLinks';
import { TechStack } from './components/TechStack';
import { Timeline } from './components/Timeline';

## 👋 嗨,我是 Sai

<SocialLinks />

## 🛠 技能清單

<TechStack 
  languages={["React", "TypeScript", "TailwindCSS", "Node.js"]} 
/>

## ⏳ 成長軌跡

<Timeline 
  items={[
    {
      year: "2023 - 現在",
      title: "踏入程式開發領域",
      description: "專注於構建高效能的 React 應用...",
      badge: "進行中"
    }
  ]} 
/>

成果展示

最終實現的效果:

SocialLinks

  • 四個社群平台按鈕(GitHub, Twitter, Email, LinkedIn)
  • 每個按鈕 hover 時有專屬配色(灰、藍、紅、藍)
  • 平滑的縮放動畫

TechStack

  • 彩色技能標籤,自動換行
  • 明確的視覺層次感
  • 在白色背景下有極佳的可讀性

Timeline

  • 垂直時間軸設計
  • 圓圈節點 hover 時放大發光
  • 彩色 badge 顯示狀態(進行中、已完成、起點)

效能考量

使用 Astro 的好處是這些 React 組件只在必要時才載入 JavaScript:

# 查看建置結果
pnpm build

# 頁面體積分析
 /posts/about/index.html (+52ms)
  - Static HTML: 98%
  - JavaScript: 2%  # 只有互動組件需要 JS

對比傳統的純 React SPA,這種 Partial Hydration 策略讓頁面載入速度快得多。


總結與反思

這次學到的重點

  1. 配色永遠比想像中重要 - 好的 UI 需要足夠的對比度
  2. MDX 有自己的規則 - Import 順序不能亂
  3. Astro Islands 是真的香 - 在需要的地方用 React,其他部分保持靜態

可以改進的地方

  • 增加響應式斷點,手機版調整版面
  • Timeline 可以加入展開/收合功能
  • SocialLinks 圖標可以加載失敗處理

下一步計畫

  • 為其他頁面添加更多互動組件
  • 實作深色模式切換(目前只針對白色背景優化)
  • 考慮使用 Framer Motion 增加更流暢的動畫

相關資源

如果你也在打造自己的個人網站,希望這篇文章對你有幫助!🚀