實戰範例 07:響應式導覽列與行動選單改版
將專案的導覽列改版為響應式設計,在行動裝置上摺疊為漢堡選單,點擊展開為滑出式抽屜。
/opsx:propose/opsx:apply/opsx:archive 實戰背景
在行動裝置流量大於桌面端的時代,網站的導覽體驗 (Navigation Experience) 至關重要。桌面端的水平導覽列在手機等窄螢幕畫面上會因空間不足而重疊擠壓,此時必須使用響應式斷點將其改為摺疊式的漢堡選單 (Hamburger Menu)。
本範例中,我們將重構網頁頂部導覽列,實作在平板與手機視窗(寬度小於 768px)下自動隱藏選單,並在點擊選單按鈕時以抽屜式 (Drawer) 動態覆蓋全螢幕。我們將使用 OpenSpec 的 快速開發工作流 (Quick Flow) 來進行此項任務。
規格定義
本次變更計畫的 Capability 为 user-interface。我們預計在規格增量 (Spec Delta) 中定義以下驗收標準:
## MODIFIED Requirements
### Requirement: Responsive navigation header
The main page header SHALL adapt its navigation items based on viewport widths, utilizing a collapsible overlay drawer on mobile viewports.
#### Scenario: Mobile drawer toggle
- **WHEN** the viewport is narrower than 768px and the hamburger toggle is tapped
- **THEN** the navigation links SHALL slide in as an overlay, and the body scroll SHALL be disabled
端到端 6 輪 AI 協作紀錄
以下是 You 與 AI 助手的完整對話歷史。
── 第 1 輪:建立計畫與提案 ──
- You:
/opsx:propose "將 Navbar 改為響應式,手機上變漢堡選單,點擊後選單從左側滑入,並且不能滑動背景網頁" - AI:
好的,已建立變更計畫
redesign-responsive-navbar:- 建立 proposal.md: 說明為什麼要改版 Navbar,以及在行動端禁用背景滾動的機制。
- 建立 specs/user-interface/spec.md: 新增手機漢堡選單與滾動鎖定 (Scroll Lock) 的驗收情境 (Scenario)。
- 建立 tasks.md: 包含以下 4 個實作任務:
- 1.1 建立
Hamburger.astro按鈕元件 - 1.2 建立 CSS 滑出式 Drawer 與選單結構
- 1.3 撰寫控制選單開關與鎖定背景滾動的 JavaScript
- 1.4 撰寫響應式寬度的測試案例
- 1.1 建立
規劃就緒!請執行
/opsx:apply開始實作。
── 第 2 輪:審查與微調提案 ──
- You:
滑出動畫要使用 Tailwind CSS 的 transition-transform 與 duration-200,不要自己寫 keyframes。 - AI:
好的,我已透過
/opsx:update更新文件:- 更新 design.md:規劃使用 Tailwind
translate-x-full作為初始位置,在active時移去該類別,並搭配duration-200實作 smooth slide-in。 - 更新 tasks.md:將任務 1.2 設定為「使用 Tailwind 樣式實作滑出效果」。
一致性更新完成。現在您可以執行
/opsx:apply了。 - 更新 design.md:規劃使用 Tailwind
── 第 3 輪:實作任務 1 (Hamburger 按鈕) ──
- You:
/opsx:apply - AI:
Implementing: redesign-responsive-navbar (schema: spec-driven)
正在處理任務 1/4:建立
Hamburger.astro按鈕元件。我已在
src/components/Hamburger.astro中完成了按鈕實作:<button id="menu-toggle" class="block md:hidden p-2 text-gray-600 focus:outline-none" aria-label="打開選單"> <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /> </svg> </button>✓ 任務已完成。已將 tasks.md 中的任務 1.1 標記為完成。
準備好進入任務 1.2「建立滑出式 Drawer」了嗎?
── 第 4 輪:實作任務 2 (滑出式 Drawer) ──
- You:
繼續實作 Drawer。 - AI:
Implementing: redesign-responsive-navbar
正在處理任務 2/4:建立 CSS 滑出式 Drawer 與選單結構。
我已在
src/components/Navbar.astro中加入了手機選單容器,使用 Tailwind 樣式:<!-- Drawer容器 --> <div id="mobile-drawer" class="fixed inset-y-0 right-0 z-50 w-64 translate-x-full bg-white shadow-xl transition-transform duration-200 ease-in-out md:hidden"> <!-- 手機版選單連結 --> </div> <!-- 背景 Backdrop 遮罩 --> <div id="drawer-backdrop" class="fixed inset-0 z-40 hidden bg-black/40 md:hidden"></div>✓ 任務已完成。已將任務 1.2 標記為完成。
下一步將撰寫 JavaScript 控制邏輯與滾動鎖定。是否繼續?
── 第 5 輪:實作任務 3 (JavaScript 控制邏輯) ──
- You:
繼續實作控制 JS。 - AI:
Implementing: redesign-responsive-navbar
正在處理任務 3/4:撰寫控制選單開關與鎖定背景滾動的 JavaScript。
我已在
src/components/Navbar.astro底下加入了控制指令碼,並實作了背景滾動鎖定 (Scroll Lock) 的邏輯:const toggle = document.getElementById('menu-toggle'); const drawer = document.getElementById('mobile-drawer'); const backdrop = document.getElementById('drawer-backdrop'); function setOpen(open) { drawer.classList.toggle('translate-x-full', !open); backdrop.classList.toggle('hidden', !open); // 鎖定/解鎖背景滾動,避免行動端滾動穿透 (Scroll Penetration) document.body.style.overflow = open ? 'hidden' : ''; } toggle.addEventListener('click', () => setOpen(true)); backdrop.addEventListener('click', () => setOpen(false));✓ 任務已完成。已將任務 1.3 標記為完成。
剩下最後一個測試任務。是否繼續?
── 第 6 輪:撰寫測試、驗證與歸檔 ──
- You:
執行測試,verify 並歸檔。 - AI:
Implementing: redesign-responsive-navbar
正在處理任務 4/4:撰寫響應式寬度的測試案例。
我已在
tests/navbar.test.ts中撰寫測試,使用 Playwright 模擬手機尺寸的視窗,觸發點擊並檢查body的overflow屬性是否變為hidden。測試成功通過。✓ 4/4 任務全數完成。
自動驗證與歸檔:
- 驗證成功:實作完全符合
Scenario: Mobile drawer toggle。 - 智慧同步:已將暫存規格合併至主規格 openspec/specs/user-interface/spec.md。
- 目錄歸檔:已將計畫資料夾移至
openspec/changes/archive/2026-07-14-redesign-responsive-navbar/。
Archive Complete
變更計畫已成功歸檔!
- 驗證成功:實作完全符合