跳到主要內容

FE Day 8 樣式方案:Tailwind 與設計慣例



FE Day 8 樣式方案:Tailwind 與設計慣例

執行需求:CPU 可跑。今天是「前端開發實戰:React 與 Next.js 全套」系列的第八篇。Day 5 到 Day 7 我們把元件、state、渲染模型都學會了,但寫出來的畫面還是「工程師風格」的——預設顏色、預設字級、沒有設計感。今天要解決這個問題:認識前端主流樣式方案、理解為什麼選 Tailwind CSS、學會 Tailwind 4 的 CSS-first 設定、掌握幾個常見的設計慣例(design token、間距尺度、字體階梯、響應式)。讀完之後,你應該能把前幾天寫的元件套上 Tailwind、寫出有設計感的畫面,並能用設計系統的角度思考「這份 UI 的風格怎麼統一」。整篇閱讀時間約三十五分鐘,動手做約二十五分鐘。

引言

後端工程師第一次碰前端樣式,常會卡在「要選什麼方案」。CSS Modules、SASS、styled-components、Emotion、Stitches、Tailwind——每個都有人推薦,每個都有擁護者。事實上沒有一個方案「最好」,只有「最適合這個專案」。本系列選擇 Tailwind,是因為它在 2026 年的 React 生態裡是最主流的選擇(Vercel、Next.js、shadcn/ui、Tremor 等社群套件都內建支援),而且它跟 React 元件設計的理念非常契合:把樣式視為元件的「組成部分」,而不是分開的 CSS 檔。

今天要解開五個疑問:第一,前端樣式方案的分類與取捨;第二,Tailwind 4 在 2025 年初發佈後有什麼新設計;第三,CSS-first 設定怎麼寫(取代 v3 的 tailwind.config.js);第四,utility class 怎麼跟 React 元件搭配(特別是「有條件的 className」);第五,設計系統與 design token 的概念,怎麼把 Tailwind 的設定檔變成團隊的「視覺字典」。最後用一個完整的「預約卡片」範例把前述觀念串起來,並展示 Tailwind 4 的自訂主題怎麼用。

前端樣式方案的分類與取捨

前端樣式方案大致可以分成五大類,各有不同的設計哲學:

  • 全域 CSS:寫在 .css 檔、用 class selector 套用。最傳統但難以模組化,class 名稱容易衝突。適合非常小的專案或純行銷頁。
  • CSS Modules:把 CSS 檔綁定到特定元件(Button.module.css),class 名稱會被編譯器改寫成唯一值,避免衝突。適合喜歡「CSS 跟 JS 分開」的團隊。
  • CSS-in-JS:styled-components、Emotion 等,把樣式寫在 JS 檔裡。動態樣式能力強,但有 runtime overhead(執行時產生 CSS 字串)。React 19 之後這個類別的使用率下降。
  • Utility-first:Tailwind 主導,提供大量短小的 utility class(bg-blue-500、p-4、flex),直接在 HTML/JSX 上組合。HTML 看起來很長、但元件檔案就是「樣式 + 結構」一站搞定。
  • 元件庫 + 主題:shadcn/ui、Mantine、Chakra 等,預先寫好元件、按主題切換外觀。適合需要快速打造 MVP 的團隊。

2026 年的主流選擇是「Tailwind + 輕量元件庫」。Vercel 的官方樣板(next-app-tailwind)預裝 Tailwind 4,shadcn/ui 把 Tailwind 推到主流,Vite 的 React 樣板也內建。Tailwind 4 在 2025 年初發佈後,做了兩個關鍵改變:CSS-first 設定(不再需要 tailwind.config.js)與更快的編譯速度(用 Rust 寫的引擎)。本系列從 Day 8 之後所有元件都會用 Tailwind 4。

Tailwind 4 的 CSS-first 設定

Tailwind 4 最顯眼的改變是「設定檔從 JavaScript 搬到 CSS」。v3 時代你會寫 tailwind.config.js 設定主題、擴充顏色、調整斷點;v4 把這些都搬到 CSS 檔裡,用 @theme 區塊宣告 design tokens。

/* src/styles/app.css */
/* Tailwind 4 的 CSS-first 設定 */
@import "tailwindcss";

/* 用 @theme 定義設計 token:顏色、字體、間距尺度 */
@theme {
  /* 顏色:對應 bg-xxx、text-xxx、border-xxx */
  --color-brand-50:  #f0f9ff;
  --color-brand-500: #0ea5e9;
  --color-brand-900: #0c4a6e;

  /* 字體:對應 font-sans、font-mono */
  --font-sans: "Inter", "Noto Sans TC", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;

  /* 間距尺度:對應 p-4、m-2、gap-6 */
  --spacing-1: 0.25rem;
  --spacing-2: 0.5rem;
  --spacing-4: 1rem;
  --spacing-8: 2rem;

  /* 斷點:對應 sm:、md:、lg: */
  --breakpoint-sm: 640px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 1024px;
}

這份 CSS 檔做了三件事:第一,@import "tailwindcss" 把 Tailwind 的 base、components、utilities 一次引入(v3 要分三行);第二,@theme 區塊宣告這個專案的設計 token,這些 token 會自動生成對應的 utility class(--color-brand-500 → bg-brand-500、text-brand-500 等);第三,不用 tailwind.config.js——所有設定都在 CSS 裡完成。

實務上有兩個注意事項:第一,@theme 區塊必須放在 @import "tailwindcss" 之後;第二,設計 token 的命名要遵循 Tailwind 慣例——顏色用 --color-xxx-yyy、字體用 --font-xxx、間距用 --spacing-xxx,這樣 Tailwind 才能正確產生 utility class。

用 utility class 組合元件樣式

Tailwind 的核心是「用 utility class 直接在 JSX 上描述樣式」。下面是一個按鈕元件的寫法:

// src/components/Button.tsx
// 用 Tailwind 4 寫按鈕元件
import type { ReactNode } from "react";

type ButtonProps = {
  children: ReactNode;
  variant?: "primary" | "secondary" | "ghost";
  onClick?: () => void;
};

export function Button({ children, variant = "primary", onClick }: ButtonProps) {
  // 基礎 class(所有 variant 都有)
  const base = [
    "inline-flex items-center justify-center",
    "px-4 py-2 rounded-md",
    "text-sm font-medium",
    "transition-colors",
    "focus-visible:outline-2 focus-visible:outline-offset-2",
  ];

  // 根據 variant 決定顏色
  const variantClass =
    variant === "primary"
      ? "bg-brand-500 text-white hover:bg-brand-900"
      : variant === "secondary"
      ? "bg-slate-200 text-slate-900 hover:bg-slate-300"
      : "bg-transparent text-slate-700 hover:bg-slate-100";

  return (
    <button type="button" onClick={onClick} className={[...base, variantClass].join(" ")}>
      {children}
    </button>
  );
}

這支 Button 展示四個 Tailwind 慣例:第一,className 用陣列 .join(" ") 串接多個條件 class,這比三元鏈好讀;第二,顏色 hover 狀態用 hover:bg-brand-900、focus 用 focus-visible:outline-2,Tailwind 把狀態當作前綴;第三,spacing scale 用 px-4 py-2(padding-x 1rem、padding-y 0.5rem)——這對應 @theme 裡的 --spacing-4;第四,rounded-md、text-sm、font-medium 等尺寸都來自設計 token,保證整個專案的視覺一致。

另一個常見的寫法是用 clsx 套件處理條件 class:

// 用 clsx 處理條件 class
import clsx from "clsx";

type CardProps = {
  title: string;
  active?: boolean;
};

export function Card({ title, active = false }: CardProps) {
  return (
    <div
      className={clsx(
        "rounded-lg border p-4 transition",
        active ? "border-brand-500 bg-brand-50" : "border-slate-200 bg-white",
      )}
    >
      <h3 className="text-lg font-semibold">{title}</h3>
    </div>
  );
}

clsx 的好處是處理「條件 class」更直觀:clsx("base", cond && "extra")。當條件多、需要組合時(例如「按鈕尺寸 × variant × disabled」),clsx 比陣列.join 容易讀。實務上大型專案幾乎都裝 clsx,Day 32 設計系統時會大量用到。

設計系統與設計 token

「設計系統」(design system)是「把視覺決策變成可重用的 token」的過程。它讓「按鈕的圓角」、「標題的字級」、「卡片之間的間距」這些「每頁都要重複決定」的事變成「從設定檔撈出來」。Tailwind 的 @theme 就是「設計系統的程式碼版本」。

一份完整的設計系統通常包含以下幾個維度:

  • 顏色系統:品牌色(brand)、狀態色(success、warning、danger、info)、中性色(slate、gray)。每種顏色通常有 50–900 的 10 階色票。
  • 字體階梯:字級(xs、sm、base、lg、xl、2xl、3xl)、行高(leading-tight、leading-relaxed)、字重(font-normal、font-medium、font-semibold、font-bold)。
  • 間距尺度:4 倍數或 8 倍數的階梯(4px、8px、12px、16px、24px、32px、48px、64px),避免「自由發揮」的間距。
  • 圓角尺度:none、sm、md、lg、full。圓角大會讓 UI 看起來「圓潤」,小會讓 UI 看起來「俐落」。
  • 陰影尺度:sm、md、lg、xl。陰影給「卡片浮起來」的感覺,但不能過度。

把這套 token 寫進 @theme 之後,所有元件都「自動遵守」這份設計。例如:全站按鈕都用 rounded-md(中等圓角),就永遠不會出現「這頁按鈕圓一點、那頁方一點」的混亂。Day 32 會展開完整的設計系統實作,這裡先建立概念。

設計 token 的命名慣例

設計 token 的命名會直接影響 utility class 的可讀性。常見的命名模式:

/* 顏色:用途 + 色階 */
--color-brand-500: #0ea5e9;
--color-danger-700: #b91c1c;
--color-canvas:   #ffffff;        /* 背景色,不分階 */

/* 字體:用途 */
--font-sans:  "Inter", system-ui, sans-serif;
--font-mono:  "JetBrains Mono", monospace;
--font-serif: "Noto Serif TC", serif;

/* 字級:用途 + 大小 */
--text-xs:   0.75rem;
--text-sm:   0.875rem;
--text-base: 1rem;
--text-lg:   1.125rem;
--text-xl:   1.25rem;
--text-2xl:  1.5rem;

/* 圓角:用途 */
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 1rem;
--radius-full: 9999px;

/* 陰影:用途 */
--shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px rgb(0 0 0 / 0.1);

Tailwind 4 會把這些 token 自動轉成 utility class:--color-brand-500 → bg-brand-500、--radius-md → rounded-md、--shadow-lg → shadow-lg。命名越語意化、未來改色或換主題時越容易。例如品牌色叫 brand 而不是 blue,未來從藍色換成綠色時只要改 @theme 的值,不用改元件檔。

響應式與無障礙的 Tailwind 寫法

Tailwind 把響應式與無障礙的設計也內建到 utility class 裡。第一,響應式前綴:sm:、md:、lg:、xl: 對應到 @theme 裡的 breakpoint。寫法是「在某個斷點以上套用樣式」:

// src/components/Hero.tsx
// 響應式寫法:手機單欄、桌機雙欄
export function Hero() {
  return (
    <section className="grid grid-cols-1 gap-6 p-6 md:grid-cols-2 md:gap-8 md:p-12">
      <div>
        <h1 className="text-2xl font-bold md:text-4xl">預約管理系統</h1>
        <p className="mt-2 text-slate-600 md:mt-4 md:text-lg">
          用最少時間處理最多預約
        </p>
      </div>
      <div className="flex items-center justify-center">
        <img src="/hero.svg" alt="預約系統示意" className="w-full max-w-md" />
      </div>
    </section>
  );
}

這段 Hero 區塊展示三個響應式技巧:grid-cols-1 md:grid-cols-2 在 md 以上變兩欄;p-6 md:p-12 在 md 以上加大 padding;text-2xl md:text-4xl 在 md 以上放大標題。手機優先(mobile first)是 Tailwind 的預設——沒寫前綴的 class 適用所有螢幕,加前綴代表「這個尺寸以上才套用」。

第二,無障礙前綴:focus-visible:、hover:、active:、disabled:。這些是 Tailwind 的狀態變體(variants),讓你直接用 class 寫互動樣式:

// src/components/SubmitButton.tsx
// 互動狀態 + 無障礙
type Props = {
  isSubmitting: boolean;
  children: React.ReactNode;
};

export function SubmitButton({ isSubmitting, children }: Props) {
  return (
    <button
      type="submit"
      disabled={isSubmitting}
      className={[
        "rounded-md bg-brand-500 px-4 py-2 text-white",
        "hover:bg-brand-700",
        "active:bg-brand-900",
        "disabled:cursor-not-allowed disabled:opacity-50",
        "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-500",
      ].join(" ")}
    >
      {isSubmitting ? "送出中…" : children}
    </button>
  );
}

這段按鈕展示完整的互動 + 無障礙樣式:hover:bg-brand-700 滑鼠移過去變深;active:bg-brand-900 按下去更深;disabled:opacity-50 停用時變透明;focus-visible:outline-2 鍵盤 focus 時顯示外框(符合 WCAG 規範)。Day 28 會專門展開無障礙,這裡先記得「focus-visible:」這個 class 是寫鍵盤可用 UI 的好朋友。

完整實作:預約卡片元件

把今天學的東西整合起來:寫一個「預約卡片」元件,展示設計 token、響應式、條件 class 的綜合運用:

// src/features/booking/BookingCard.tsx
// 預約卡片:使用設計 token、響應式、條件 class
import clsx from "clsx";
import type { Booking, BookingStatus } from "../../types/booking";

type BookingCardProps = {
  booking: Booking;
  onCancel?: (id: number) => void;
};

// 狀態對應的視覺:背景色、邊框色、文字色
const statusStyles: Record<BookingStatus, string> = {
  pending:   "bg-amber-50 border-amber-300 text-amber-900",
  confirmed: "bg-emerald-50 border-emerald-300 text-emerald-900",
  cancelled: "bg-slate-100 border-slate-300 text-slate-600",
  completed: "bg-sky-50 border-sky-300 text-sky-900",
};

const statusLabel: Record<BookingStatus, string> = {
  pending:   "待確認",
  confirmed: "已確認",
  cancelled: "已取消",
  completed: "已完成",
};

export function BookingCard({ booking, onCancel }: BookingCardProps) {
  return (
    <article
      className={clsx(
        // 基礎樣式:圓角、邊框、padding、陰影
        "rounded-lg border p-4 shadow-sm",
        // 響應式:手機縱向、桌機橫向
        "flex flex-col gap-3 md:flex-row md:items-center md:justify-between",
        // 狀態對應的顏色(用設計 token)
        statusStyles[booking.status],
      )}
    >
      {/* 左側:預約資訊 */}
      <div className="space-y-1">
        <h3 className="text-lg font-semibold">
          {booking.customerName}
          <span className="ml-2 text-sm font-normal opacity-70">
            #{booking.id}
          </span>
        </h3>
        <p className="text-sm opacity-80">
          {booking.customerPhone}・{booking.startAt}
        </p>
        <span className="inline-block rounded-full border px-2 py-0.5 text-xs">
          {statusLabel[booking.status]}
        </span>
      </div>

      {/* 右側:操作按鈕 */}
      {onCancel && booking.status !== "cancelled" && (
        <button
          type="button"
          onClick={() => onCancel(booking.id)}
          className={clsx(
            "rounded-md border px-3 py-1.5 text-sm",
            "border-slate-300 bg-white text-slate-700",
            "hover:bg-slate-50 active:bg-slate-100",
            "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-500",
          )}
        >
          取消預約
        </button>
      )}
    </article>
  );
}

這支 BookingCard 展示八個設計觀念:第一,clsx 處理「基礎 + 響應式 + 狀態顏色」三組 class 的組合;第二,Record<BookingStatus, string> 確保四種狀態都有對應樣式,TypeScript 在編譯期就會抓出漏寫的狀態;第三,狀態樣式集中在 statusStyles 物件,未來改色只改一處;第四,flex flex-col md:flex-row 從手機直式切換到桌機橫式;第五,opacity-70、opacity-80 處理次要文字的視覺權重;第六,focus-visible:outline-2 讓鍵盤使用者看到 focus 框;第七,shadow-sm 讓卡片「浮起來」,但不過度;第八,整體只用 Tailwind class,沒有額外的 CSS 檔——設計 token 改了就全站跟著改。

常見錯誤與踩雷

第一次用 Tailwind 4 最常踩的雷有四個。第一個是「設定沒生效」。症狀:@theme 寫了 --color-brand-500 但 bg-brand-500 沒出現。原因可能是:@theme 寫在 @import "tailwindcss" 之前、或是命名不符合慣例(少了 color-、spacing- 等前綴)。修法是確認順序、檢查命名、重新編譯。

第二個是「忘記 utility class 不會被 purge」。Tailwind v4 預設會掃描專案內所有檔案,找出實際用到的 utility class。如果某個 class 是「執行時動態組合」(例如 className={`p-${size}`}),它不會被識別。修法是在 @source 區塊明確列出會動態產生的檔案,或改用 safelist:

/* src/styles/app.css */
/* 明確告訴 Tailwind 掃描哪些檔案 */
@import "tailwindcss" source("../src/**/*.{ts,tsx}");

第三個是「忘記寫斷點前綴」。新手寫 md:grid-cols-2 時以為「手機會變兩欄」,實際上 Tailwind 是 mobile-first——md: 代表「md 以上才兩欄」,手機是一欄。如果要「手機兩欄、桌機一欄」要用 max-md:grid-cols-2 這類反向變體。

第四個是「className 太長難讀」。當元件樣式很複雜,把所有 class 寫在 JSX 上會讓程式碼很亂。修法有兩個:把基礎 class 抽成常數(const base = "...");用 clsx 處理條件組合;或用 @apply 在 CSS 裡定義元件 class:

/* 在 CSS 裡定義可重用的元件 class */
@layer components {
  .btn-primary {
    @apply inline-flex items-center justify-center rounded-md bg-brand-500 px-4 py-2 text-white hover:bg-brand-700;
  }
}

但要注意:@apply 寫太多會讓你「重新發明 CSS Modules」——失去了 Tailwind 集中管理的優勢。建議只在「跨多個元件共用、又有條件差異」的情境用 @apply,否則直接寫 className。

效能與實務提醒

Tailwind 4 的編譯速度比 v3 快很多(用 Rust 引擎),但「class 名稱很長」這件事還是會讓 HTML 變肥。實務上有兩個技巧可以降低 bundle size:第一,使用 PurgeCSS 機制(v4 內建),它會自動把沒用到的 class 從最終 CSS 裡刪掉,確保 CSS 檔案只包含實際用到的部分;第二,避免在元件裡寫 style={{...}} 內嵌樣式,這會繞過 Tailwind 的 purge 機制。

另一個實務提醒是「設計 token 的顏色要符合對比」。WCAG AA 規範要求文字與背景的對比至少 4.5:1(一般文字)或 3:1(大字)。這不是「設計感」的問題,是「無障礙」的問題——視障、色弱、銀髮使用者都需要足夠對比才能讀。Tailwind 的色票(slate-900 配 slate-50)已經符合對比,但自訂顏色(--color-brand-500)要自己驗證。可以用 Stark、WebAIM Contrast Checker 等工具測試。Day 28 與 Day 42 會再展開無障礙細節。

第三個提醒是「不要過度使用響應式 class」。每加一個 md:、lg:,CSS 體積就會膨脹。如果設計稿只有「手機 / 桌機」兩種尺寸,就用 md: 一個斷點就好;如果有「手機 / 平板 / 桌機 / 大螢幕」四種,再用 sm: md: lg: xl:。盲目堆斷點會讓 CSS 變肥、也讓元件難以預測。Day 25 會展開 Core Web Vitals 與 bundle size 的取捨。

最後一個重點是「設計系統要先內部約定」。寫 @theme 之前,先和設計師(如果有的話)或團隊對齊:色票要哪些?字級要幾階?間距尺度是 4 倍數還是 8 倍數?圓角大小?陰影規範?把這些決定寫進 design token 之後,所有元件都遵守,整個專案的視覺就會一致。沒有先約定就寫 Tailwind,最後會出現「這頁用 slate、那頁用 gray、第三頁自訂顏色」的混亂。Day 32 會展開完整的設計系統建置流程。

把設計 token 變成 preset:團隊共用

如果整個團隊要把 Tailwind 的設計 token 統一管理,最常見的做法是「把 @theme 區塊抽成一個獨立的 CSS 檔,再讓各專案 import」。這個獨立檔通常叫 tailwind-preset.css,放在公司內部的 npm 套件或 monorepo 的共享目錄裡:

/* packages/design-system/tailwind-preset.css */
/* 團隊共用的 Tailwind 4 設計 token */
@import "tailwindcss";

@theme {
  /* 品牌色票:對應 bg-brand-xxx、text-brand-xxx */
  --color-brand-50:  #f0f9ff;
  --color-brand-100: #e0f2fe;
  --color-brand-500: #0ea5e9;
  --color-brand-700: #0369a1;
  --color-brand-900: #0c4a6e;

  /* 狀態色:對應 bg-success、bg-warning、bg-danger */
  --color-success: #10b981;
  --color-warning: #f59e0b;
  --color-danger:  #ef4444;

  /* 字體階梯:對應 text-sm、text-lg 等 */
  --font-sans:  "Inter", "Noto Sans TC", system-ui, sans-serif;
  --text-xs:   0.75rem;
  --text-sm:   0.875rem;
  --text-base: 1rem;
  --text-lg:   1.125rem;
  --text-xl:   1.25rem;
  --text-2xl:  1.5rem;

  /* 間距尺度(4 倍數) */
  --spacing-1: 0.25rem;
  --spacing-2: 0.5rem;
  --spacing-4: 1rem;
  --spacing-8: 2rem;
  --spacing-12: 3rem;

  /* 圓角 */
  --radius-sm:   0.25rem;
  --radius-md:   0.5rem;
  --radius-lg:   1rem;
  --radius-full: 9999px;

  /* 斷點:對應 sm:、md:、lg: 前綴 */
  --breakpoint-sm: 640px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 1024px;
}

各專案的 app.css 只要一行就能繼承整套 token:

// src/styles/app.css
// 載入團隊 preset + 專案自訂的覆寫
@import "@company/design-system/tailwind-preset.css";

@theme {
  /* 專案自訂:覆寫部分 token */
  --color-brand-500: #14b8a6;  /* 專案 A 用綠色品牌 */
}

這種 preset 模式讓團隊的視覺語言集中管理:設計師改色票時只需要改 preset,各專案 npm update 後視覺就統一更新。實務上大型組織(電商、金融、SaaS)幾乎都會建立自己的 design system 套件,避免每個專案各寫各的 Tailwind 設定。Day 32 會展開完整的設計系統建置流程,包含 token、preset、Storybook、CSS 變數怎麼互通。

另一個延伸主題是「元件庫」。如果 preset 是「視覺字典」,那元件庫就是「視覺單字」。shadcn/ui 是 2026 年最主流的 React 元件庫之一:它不是傳統的 npm 套件(裝完才能用),而是「把元件原始碼複製到你的專案裡」(copy-paste)。這種模式讓你能 100% 客製化元件樣式,又不用從零開始寫。當 Tailwind preset 搭配 shadcn/ui 時,整個專案的視覺可以快速達成「品牌一致 + 工程一致」的雙重目標。

最後回顧一下今天的學習地圖:我們從樣式方案的分類開始,理解 Tailwind 4 為什麼成為主流;接著動手寫 CSS-first 設定,把設計 token 變成可重用的 utility class;再學會 utility class 與元件搭配的慣例(條件 class、clsx);設計系統的概念也建立了(色票、字級、間距尺度);最後用完整的預約卡片把所有觀念串起來。這套方法論是 Day 32 設計系統章節的基礎,Day 28 無障礙、Day 25 效能優化也都會反覆提到。寫完今天的範例,你應該已經能獨立產出「有設計感、有系統性」的前端畫面——這是後端工程師跨足全端開發的一個重要里程碑。

小結

今天我們把前端樣式方案拆開來看:分類與取捨、Tailwind 4 的 CSS-first 設定、utility class 與元件搭配、條件 class 的處理(clsx)、設計系統與 design token 的概念、響應式與無障礙的 Tailwind 寫法。最後用一個完整的「預約卡片」元件把所有觀念串起來。明天 Day 9 進入 useEffect:副作用的處理、依賴陣列的規則、cleanup 的角色,以及「為什麼 useEffect 不是生命週期方法」。Day 9 結束時,你的元件就能跟外部世界(API、計時器、localStorage)互動了。

結語

明天,我們會把 useEffect 一次學完:什麼是副作用、effect 的 callback 什麼時候執行、依賴陣列的規則、cleanup 為什麼重要、什麼情境根本不該用 useEffect。Day 9 是 React Hooks 的關鍵轉折——從「純函式元件」跨到「能跟外部世界溝通的元件」。接著 Day 10 進入自訂 Hook,把 useEffect、useState 等組合起來變成可重用的積木。

延伸資源

  • Tailwind CSS 官方(v4 說明):https://tailwindcss.com/docs/installation
  • Tailwind CSS 官方〈Theme variables〉:https://tailwindcss.com/docs/theme
  • Tailwind CSS 官方〈Responsive design〉:https://tailwindcss.com/docs/responsive-design
  • shadcn/ui 元件庫(Tailwind 4 + Radix UI):https://ui.shadcn.com/
  • MDN〈CSS Custom Properties〉:https://developer.mozilla.org/zh-TW/docs/Web/CSS/--*

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...