📱 第十三章:RWD 響應式設計與魔術漢堡選單

你花了好幾個小時,在你的 27 吋螢幕上把露營網站雕琢得美輪美奐。 然後你興高采烈地傳給老闆,老闆用 iPhone 點開網址,眉頭一皺:「這什麼鬼?文字全部疊在一起,圖片也超出去螢幕了!」

這就是所有前端工程師的惡夢:沒有做響應式設計 (Responsive Web Design, 簡稱 RWD)

在過去,要讓網站適應手機、平板、電腦三種螢幕,你需要寫好幾個 media query CSS 檔案,非常痛苦。 但現在,我們有 Tailwind CSS!加上 AI 的輔助,RWD 將變成一塊小蛋糕。

這堂課,我們將用 5 個 Vibe Prompt,教你如何一秒修復手機版破版,並實作出那個經典的「漢堡選單 (Hamburger Menu)」!


📐 實戰 1:掌握 Tailwind 的斷點魔法

在 Tailwind 中,最核心的 RWD 概念就是加上前綴字:

  • 什麼都沒加 (預設) = 手機版 (Mobile-first 思維)
  • md: = 平板版 (Medium)
  • lg: = 電腦版 (Large)

💡 Vibe Prompt 實戰 1:一秒解決文字擠成一團的問題

[!IMPORTANT] 請複製以下 Prompt 傳送給 AI:

我在設計露營網站的標題。目前的 class 是:text-6xl font-bold text-center 在電腦上看很霸氣,但在手機上看,字體太大直接超出版面了! 請幫我用 Tailwind 的 RWD 寫法 (sm, md, lg) 來修改。 需求: 1. 手機版 (預設) 時字體小一點,用 text-3xl。 2. 平板以上 (md:) 用 text-5xl。 3. 電腦以上 (lg:) 用 text-7xl。 請直接給我修改後的那一行 div。

🤖 AI 生成的響應式程式碼:

<!-- 舊的寫法:手機直接爆版 -->
<h1 class="text-6xl font-bold text-center">探索未知的營地</h1>

<!-- 🔥 AI 修改後的 RWD 寫法:隨螢幕自動縮放! -->
<h1 class="text-3xl md:text-5xl lg:text-7xl font-bold text-center">探索未知的營地</h1>

🔍 深度解析: 這就是「Mobile-first (手機優先)」思維。我們一開始設定的 text-3xl 是給手機看的。當螢幕變大,跨過 md: (平板) 的斷點時,Tailwind 就會自動把它覆蓋成 text-5xl。你再也不用自己去寫複雜的 @media (min-width: 768px) 了!


🍔 實戰 2:從無到有打造「魔術漢堡選單」

在電腦版,我們的導覽列 (Navbar) 有很多按鈕:「首頁」、「營地列表」、「登入」、「註冊」,它們可以橫排顯示。 但在手機版,寬度根本不夠塞!這時候,我們需要一個「三條線的漢堡圖示 (Hamburger Icon)」。點擊它,選單才會滑出來。

這牽涉到 React 的狀態 (State) 切換。這對新手來說很難,但對 AI 來說易如反掌。

💡 Vibe Prompt 實戰 2:指揮 AI 寫出有邏輯的漢堡選單

[!IMPORTANT] 請複製以下 Prompt 傳送給 AI:

我要用 React 實作一個導覽列 (Navbar)。 需求如下: 1. 在電腦版 (md: 以上),選單連結 ("首頁", "探索", "登入") 橫向排列在右側。 2. 在手機版,隱藏這些連結,並顯示一個「漢堡圖示按鈕 (三條水平線)」。 3. 請使用 useState 宣告一個 isOpen 的狀態,預設為 false。 4. 當點擊漢堡按鈕時,切換 isOpen 的狀態。 5. 如果 isOpen 為 true,請在導覽列下方顯示一個滿版寬度的直向選單。 請給我完整的 React 元件程式碼。

🤖 AI 生成的真實程式碼 (結構解析):

import React, { useState } from 'react';

export default function Navbar() {
  // 控制手機版選單開關的靈魂!
  const [isOpen, setIsOpen] = useState(false);

  return (
    <nav className="bg-zinc-900 border-b border-zinc-800 p-4">
      <div className="max-w-6xl mx-auto flex justify-between items-center">
        {/* 左側 Logo */}
        <div className="text-xl font-bold text-white">🏕️ 露營趣</div>

        {/* 電腦版選單 (手機隱藏 md:flex,只有平板以上才顯示) */}
        <div className="hidden md:flex gap-6 text-zinc-300">
          <a href="#" className="hover:text-white">首頁</a>
          <a href="#" className="hover:text-white">探索</a>
          <a href="#" className="hover:text-white">登入</a>
        </div>

        {/* 手機版漢堡按鈕 (平板以上隱藏 md:hidden) */}
        <button 
          className="md:hidden text-white text-2xl"
          onClick={() => setIsOpen(!isOpen)}
        >
          {/* 如果打開就顯示 ✕,關閉就顯示 ☰ */}
          {isOpen ? "✕" : "☰"}
        </button>
      </div>

      {/* 手機版的直向下拉選單 (條件渲染:如果 isOpen 是 true 才顯示) */}
      {isOpen && (
        <div className="md:hidden mt-4 flex flex-col gap-4 text-center pb-4 text-zinc-300">
          <a href="#" className="hover:text-white border-b border-zinc-800 pb-2">首頁</a>
          <a href="#" className="hover:text-white border-b border-zinc-800 pb-2">探索</a>
          <a href="#" className="hover:text-white pb-2">登入</a>
        </div>
      )}
    </nav>
  );
}

✨ 實戰 3:太生硬了!給選單加上 Framer Motion 動畫

上面的漢堡選單可以運作,但它的出現方式是「瞬間閃現」,非常沒有高級感。 身為高質感的 Vibe Coder,我們不允許這種生硬的體驗存在。 我們將請出 React 動畫界的霸主:Framer Motion,讓選單用「滑出」的方式優雅登場!

💡 Vibe Prompt 實戰 3:注入百萬級滑順動畫

[!IMPORTANT] 請複製以下 Prompt 傳送給 AI:

我剛剛寫好了手機版的直向選單 (isOpen 控制顯示)。 但它是瞬間出現的,太生硬了。 請幫我引入 'framer-motion' 庫 (使用 <AnimatePresence> 和 <motion.div>)。 我要讓手機版選單出現時,有以下動畫效果: 1. 初始狀態 (initial):高度為 0,透明度為 0 (opacity: 0, height: 0) 2. 出現狀態 (animate):高度為 auto,透明度為 1 (opacity: 1, height: "auto") 3. 消失狀態 (exit):回到高度 0,透明度 0 請幫我修改剛才的 Navbar 元件。

🤖 AI 動畫大師出手:

import React, { useState } from 'react';
// 🔥 引入 Framer Motion 動畫神器
import { motion, AnimatePresence } from 'framer-motion';

export default function Navbar() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <nav className="bg-zinc-900 border-b border-zinc-800 p-4 relative z-50">
      {/* ...上面的 Logo 區塊跟按鈕都不變... */}

      {/* 使用 AnimatePresence 包裝,這是讓組件「消失時也能有動畫」的關鍵 */}
      <AnimatePresence>
        {isOpen && (
          <motion.div 
            initial={{ opacity: 0, height: 0 }}
            animate={{ opacity: 1, height: "auto" }}
            exit={{ opacity: 0, height: 0 }}
            transition={{ duration: 0.3, ease: "easeInOut" }}
            className="md:hidden overflow-hidden mt-4 flex flex-col gap-4 text-center text-zinc-300"
          >
            {/* 選單內容 */}
            <div className="py-4 space-y-4">
              <a href="#" className="block hover:text-white">首頁</a>
              <a href="#" className="block hover:text-white">探索</a>
              <a href="#" className="block hover:text-white">登入</a>
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </nav>
  );
}

🔍 深度解析: 看到了嗎?這就是價值所在!一般的新手工程師,光是為了解決「高度 0 變成 auto 的動畫無效」這個 CSS 千古難題,就會卡上三天三夜。 但你透過使用 Framer Motion 加上 AI 輔助,只要一兩分鐘,就產出了一個像是蘋果官網一樣滑順的手機下拉選單!


🚫 終極避坑指南:Z-index 疊加地獄

做了漂亮的漢堡選單下拉,最常發生的災難就是:「選單滑出來了,結果被下面的地圖或是圖片擋住!」 使用者想點「登入」,卻按到了後面的「放大地圖」按鈕。

這是因為 CSS 中的 z-index (層級) 沒有設定好。

💡 Vibe Prompt 實戰 4:解決選單被遮擋的問題

[!IMPORTANT] 請複製以下 Prompt 傳送給 AI:

我的 Navbar 漢堡選單打開時,下拉的內容被下方的一個 Leaflet 地圖區塊遮擋住了! 我該怎麼用 Tailwind 的 class 來確保我的 Navbar 永遠處於畫面的最上層? 請給我修改 <nav> 標籤的具體 Class 建議。

🤖 AI 的排版急救:

<!-- ❌ 舊的寫法:層級不夠,被地圖吃掉 -->
<nav class="bg-zinc-900 p-4">

<!-- ✅ 正確寫法:加入 relative 或 fixed,並賦予極高的 z-index -->
<nav class="bg-zinc-900 p-4 relative z-[999]">

<!--
【AI 深度解析】:
z-index (層疊順序) 就像是千層派。數字越大的在越上面。
Leaflet 地圖或某些絕對定位的圖片,預設會有 z-index (例如 400)。
如果你沒有為 Navbar 設定 z-index,它就會被地圖蓋過去。
加上 `relative z-[999]` 可以確保導覽列無論如何都在最高層級!
-->

🔒 進階體驗優化:防止選單打開時背景滾動

還有一個高級網站的隱藏細節:當手機版漢堡選單滿版打開時,如果你手滑動螢幕,背後的網站內容不應該跟著捲動!這會讓人覺得網站很廉價。

💡 Vibe Prompt 實戰 5:封鎖背景滾動

[!IMPORTANT] 請複製以下 Prompt 傳送給 AI:

當我的手機版漢堡選單打開 (isOpen 為 true) 時,我不希望使用者可以滾動背後的整個網頁。 請幫我寫一段 React 的 useEffect 邏輯。 當 isOpen === true 時,將 body 的 overflow 設為 'hidden'。 當 isOpen === false 或是元件解除安裝時,將 body 的 overflow 恢復為 'auto'。

🤖 AI 的細節體驗升級:

  // 放在 Navbar 元件內
  useEffect(() => {
    if (isOpen) {
      // 鎖死背景,禁止滾動
      document.body.style.overflow = 'hidden';
    } else {
      // 恢復正常滾動
      document.body.style.overflow = 'auto';
    }

    // 💡 清理函數 (Cleanup function):
    // 確保當使用者切換頁面、或是 Navbar 被銷毀時,不會把背景永遠鎖死
    return () => {
      document.body.style.overflow = 'auto';
    };
  }, [isOpen]); // 當 isOpen 改變時,這個 Effect 就會觸發

✅ 本章總結與實戰落幕

恭喜你完成了這門 露營官方網站架設 (Camping Official Site) 的全部 13 個章節! 我們從最基礎的 Astro 設定,一路做到了:

  1. 資料庫串接 (Supabase)
  2. 高階玻璃擬態排版 (Bento Grid)
  3. 百萬級過場動畫 (Framer Motion)
  4. 完美的手機適應與細節優化 (RWD + Scroll Lock)

現在,你不僅僅是完成了一個玩具專案,你是完成了一個**「可以拿去賣給真實客戶」的高級 SaaS 官網雛形。**

這就是 Vibe Coding 的精髓。你不必去背誦 z-index 的預設值,也不必手寫 overflow: hidden。你只需要知道**「什麼樣的細節能讓網站看起來昂貴?」** (例如:防止背景滾動、加入平滑動畫)。 當你知道了這些「商業上的需求」,你就能用精準的 Prompt 指揮 AI 幫你寫出這些程式碼。

如果你準備好挑戰更有趣的互動邏輯,歡迎進入下一個進階專案:露營定點地圖與資訊導購 (Map & AI Articles)。我們將教你如何在地圖上撒點,並串接 AI 讓它幫你自動寫露營推薦文章!我們下堂課見!

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!