iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Vibe Coding

Vibe Mode 開啟:30 天用 AI 打造網頁,邊做邊學 JavaScript系列 第 12 篇

# Day 12:UI 框架建立:結合 Tailwind CSS + shadcn/ui 建立一致的設計系統(Design System)

  • 分享至 

  • xImage
  •  

今日目標

從今天開始,我們正式邁入階段三:前端開發與 UI/UX 實作。
要讓產品擺脫「工程師審美」的黑白雜亂感,第一步不是直接刻頁面,而是打造一套統一且好擴充的 Design System(設計系統)。今天我們要運用 AI,快速結合 Tailwind CSS 與 shadcn/ui,設定好主題色系、字型與基礎 UI 組件庫!


Human vs. AI 實戰記錄

傳統做法下,光是設定 CSS 變數、安裝 component library、微調暗黑模式(Dark Mode)與設計規範就要花上大半天。現在有了 AI,我們可以 command 它是「設計師 + 前端架構師」。


1. 給 AI 的指令 (Prompt)

我們需要 AI 幫我們在 Next.js / React 專案中配置 shadcn/ui 的 CSS 變數,並建立一個具備「金融感(Financial Style)」的主題色系(以深翡翠綠/石墨灰為主色)。

你現在是一位資深 UI/UX 與前端工程師。請幫我為專案建立 Design System。

需求如下:
1. 使用 Tailwind CSS (v3/v4) 與 shadcn/ui。
2. 在 `globals.css` 中設定 CSS 變數,風格需為「現代高級金融風 (Modern Dark-friendly Finance Theme)」:
   - 主色 (Primary):高質感翡翠綠 (Emerald Green)
   - 背景色 (Background):柔和灰白 (Light Mode) / 深石墨灰 (Dark Mode)
   - 輔助色 (Accent):暖金/橙黃(用於突顯重要財務提醒)
3. 封裝一個元件範例 `<FinancialStatCard />`:
   - 包含標題、金額數字、漲跌幅 Badge、以及可選的 Icon。
   - 套用 shadcn/ui 的 Card、Badge 元件與 Tailwind Class。


2. AI 產出的結果 (Code)

AI 給出了 CSS 變數與組件寫法:

// components/FinancialStatCard.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { ArrowUpIcon, ArrowDownIcon } from "lucide-react"

interface StatCardProps {
  title: string
  amount: number
  changePercentage: number
}

export function FinancialStatCard({ title, amount, changePercentage }: StatCardProps) {
  const isPositive = changePercentage >= 0

  return (
    <Card className="bg-card text-card-foreground shadow-sm">
      <CardHeader className="flex flex-row items-center justify-between pb-2">
        <CardTitle className="text-sm font-medium text-muted-foreground">
          {title}
        </CardTitle>
        <Badge variant={isPositive ? "default" : "destructive"}>
          {isPositive ? <ArrowUpIcon className="h-4 w-4 mr-1" /> : <ArrowDownIcon className="h-4 w-4 mr-1" />}
          {Math.abs(changePercentage)}%
        </Badge>
      </CardHeader>
      <CardContent>
        <div className="text-2xl font-bold">${amount.toLocaleString()}</div>
      </CardContent>
    </Card>
  )
}


3. 修正與翻車紀錄 (Debug Experience)

這段產出看起來有模有樣,但一渲染在畫面上,立刻遇到了兩個經典的 Vibe Coding 踩坑點!

翻車點一:shadcn/ui 的 Class 合併遺漏與 Hydration Mismatch

AI 在寫條件式 Class 時,使用了字串拼接:
className={"text-2xl font-bold " + (isPositive ? "text-emerald-600" : "text-rose-600")}
這經常會因為 Tailwind 類名優先級沖突而導致樣式失效。此外,Formatting 金額(amount.toLocaleString())在 SSR(伺服器端渲染)與 CSR(用戶端渲染)時,若語系不一致會引發 Next.js 的 Hydration Mismatch Error!

翻車點二:無障礙與主題語意混淆

AI 將漲跌幅 Badge 直接套用 variant="default"(預設主色)與 variant="destructive"(紅/警告色)。但我們的 Primary 是綠色,如果上漲用 default,跌落用 destructive,當主題切換成暗色模式時,綠色層級會跟背景文字嚴重撞色,對比度不足!

介入修復

我們引導 AI 進行調整與優化:

  1. 引進 clsx + tailwind-merge (cn 工具函式),規範所有動態 Class 名稱。
  2. 加入 SSR 安全處理:封裝數字格式化函式,確保語系(Locale)設定固定為 'zh-TW' / 'USD'。
  3. 優化 HSL / OKLCH 色彩空間變數:確保 Light / Dark mode 切換時,Text 與 Background 的對比度符合 WCAG AA 規範。

修正後的程式碼:

import { cn } from "@/lib/utils" // 引入 cn 工具函式
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { TrendingUp, TrendingDown } from "lucide-react"

interface StatCardProps {
  title: string
  amount: number
  changePercentage: number
  currency?: string
}

export function FinancialStatCard({ title, amount, changePercentage, currency = 'NT$' }: StatCardProps) {
  const isPositive = changePercentage >= 0

  return (
    <Card className="border-border/60 bg-background/95 backdrop-blur-sm shadow-md transition-all hover:shadow-lg">
      <CardHeader className="flex flex-row items-center justify-between pb-2">
        <CardTitle className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
          {title}
        </CardTitle>
        <Badge 
          className={cn(
            "font-mono text-xs flex items-center gap-0.5 px-2 py-0.5",
            isPositive 
              ? "bg-emerald-500/15 text-emerald-600 dark:text-emerald-400 border-emerald-500/20" 
              : "bg-rose-500/15 text-rose-600 dark:text-rose-400 border-rose-500/20"
          )}
          variant="outline"
        >
          {isPositive ? <TrendingUp className="h-3 w-3" /> : <TrendingDown className="h-3 w-3" />}
          {isPositive ? '+' : ''}{changePercentage}%
        </Badge>
      </CardHeader>
      <CardContent>
        <div className="text-3xl font-extrabold tracking-tight font-mono">
          <span className="text-sm font-normal text-muted-foreground mr-1">{currency}</span>
          {amount.toLocaleString('zh-TW')}
        </div>
      </CardContent>
    </Card>
  )
}


JavaScript / AI 學習小結

  1. Tailwind CSS 與 CSS 變數的結合機制:
    shadcn/ui 的核心思想是「不把組件包在 npm 裡,而是將源碼直接注入專案」。它高度依賴在 globals.css 中用 HSL(或新版 OKLCH)定義 CSS 變數,並在 tailwind.config.js 引用。理解這個對接原理,比純粹叫 AI 「生成 UI」重要百倍!
  2. AI 在設計系統中的邊界:
    AI 非常擅長快速生成語意化的 HTML/CSS 標籤與基礎樣式,但在視覺卡層(Z-index)、透明度(Alpha opacity)、微互動(Hover/Active)以及多主題對比度上,經常給出「粗糙的硬編碼 (Hard-coded) Class」。工程師必須擔任最後一位品管師,透過抽象化組件(Component Abstraction)與工具函式(如 cn())建立可維護的系統。

統一的 UI 視覺風格與基礎組件庫已經建立完成!明天 Day 13,我們將指示 AI 打造 App 的核心響應式 Layout 與 Navigation 側邊欄,正式呈現應用程式的大骨架!


上一篇
# Day 11:Mock Data 生成:如何利用 AI 快速打樣測試資料庫
系列文
Vibe Mode 開啟:30 天用 AI 打造網頁,邊做邊學 JavaScript 共 12 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言