20251031 Kiro vibe coding
之前申請了 Kiro 完全沒用,趁免費時間結束前花了兩天做完 hugo-theme-deca 。心得是 AI 寫扣還是一樣爛,幫他 debug 的時間比自己寫的時間還久。
先後嘗試 spec coding 和 vibe coding,整體心得是沒有根本差異,事實上到後期 vibe coding 用的比 Kiro 內建的 spec coding 還多,反正都會錯,不如選一個廢話少的。Kiro 的 spec coding 有三個階段,分別會幫你建立 requirements.md design.md tasks.md,然後再叫他根據 task.md 完成任務,然而現在這個蠢蛋 AI 的狀況是:
- 直接叫他生成 spec 的結果肯定預期不同,因為人類很難寫的非常詳細,沒有寫的他就亂猜
- 後來想到叫 claude 生成 CLAUDE.md,這樣有正確的基礎,再餵給 Kiro 作為專案描述,結果 CLAUDE.md 寫的是對的,Kiro 建立 spec 馬上改錯
搞半天連正確的 spec 都建不起來,真的很荒謬,一度有想過要不要自己寫算了,可是後來想到,我連貼給他 CLAUDE.md 要他轉成自己的格式,這只須要一步的操作他都能出錯,那我寫老半天 spec,真到生成程式碼的時候那他還不是一樣會錯,於是果斷放棄 spec coding,vibe coding 走起。
總結這次體驗,初版是 Kiro 用 spec coding 寫出來的,後續全部使用 vibe coding,原因如上所述。初版建立期間,由於我自己也還沒想到完整專案該長怎樣,也就不太會反對 AI 程式碼,所以初版真的超級快,不過之後自己有更具體的想法之後,就會發現 AI 寫的永遠和用戶想的不一樣。
上面都只是在說 AI 寫的和人想的不一樣而已,最基本的亂寫一通問題當然也還是存在,就算貼給他文檔也一樣,這兩者的痛苦指數是伯仲之間。
不過 AI 寫有個好處就是 aria-xxx 幫你加好加滿,人類搞 aria 就超浪費時間,我從頭到尾沒要求過 aria 結果 lighthouse 測試就超過 90 分,最後稍微修改一下連 mobile 版都能四項滿分。
CLAUDE.md
# Documentation Site Architecture
**Version:** 1.0 | **Updated:** 2025-10-30
**Purpose:** Core architectural decisions for AI-assisted development
---
## Design Philosophy
3-column documentation site with CSS-only layout switching. Priorities:
1. Visual consistency across page types (content always aligned)
2. Zero JavaScript layout logic (CSS Grid + Variables)
3. Strict separation of concerns (HTML/CSS/JS boundaries)
4. Performance first (minimal reflows, efficient selectors)
---
## Layout Structure
```
┌─────────────────────────────────────────┐
│ Header (sticky, 60px) │
├──────────┬─────────────────┬────────────┤
│ Menu │ Content │ TOC │
│ 240px │ 1fr (fluid) │ 240px │
│ [sticky] │ [scrollable] │ [sticky] │
├──────────┴─────────────────┴────────────┤
│ Footer (min 80px) │
└─────────────────────────────────────────┘
Grid: var(--col-menu) 1fr var(--col-toc)
```
**Page Types:**
- `article`: 240px | 1fr | 240px (full 3-col)
- `section`: 240px | 1fr | 0 (menu + content)
- `home`/`404`: 0 | 1fr | 0 (content only)
- `blog`: 200px | 1fr | 240px (custom sidebar)
---
## Core Decisions
### 1. Layout: CSS Grid with Variable Columns
```css
.layout__body {
display: grid;
grid-template-columns: var(--col-menu, 0) 1fr var(--col-toc, 0);
}
```
**Why:** Zero JS, auto-alignment, single source of truth
**Rejected:** Flexbox with margins (requires JS, content shifts)
---
### 2. Theme: `:root[data-theme]` + Two-Layer Variables
```
Primitive Layer: --gray-50, --blue-600 (unchanging)
↓
Semantic Layer: --color-bg-base, --color-text-primary
↓
Dark Override: :root[data-theme="dark"]
```
**Why:** Components use semantic tokens only, no per-component overrides
**Rejected:** Class `.dark` (low specificity, naming collision)
**Selector Comparison:**
| Selector | Specificity | Scope | Verdict |
|----------|-------------|-------|---------|
| `:root[data-theme]` | (0,1,1) | Global | ✅ Best |
| `.dark` | (0,1,0) | Global | ❌ Weak |
| `body[data-theme]` | (0,1,1) | Limited | ❌ Wrong scope |
---
### 3. Page Control: Single `data-page-type` Attribute
```html
<body class="layout" data-page-type="article">
```
**Why:** Single source of truth, easy debugging
**Rejected:** Multiple classes (combinatorial complexity)
---
### 4. Sticky Sidebars: Native `position: sticky`
```css
.layout__sidebar {
position: sticky;
top: var(--header-height);
height: calc(100vh - var(--header-height));
}
.sidebar__scroll {
overflow-y: auto; /* Isolated scroll */
}
```
**Why:** Native performance, no JS listeners
**Rejected:** Fixed positioning (requires JS offset calc)
---
### 5. FOUC Prevention: Blocking Script
```html
<head>
<script>
(function() {
const theme = localStorage.getItem('theme') ||
(matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
})();
</script>
<link rel="stylesheet" href="styles.css">
</head>
```
**Why:** Executes before CSS parsing
**Rejected:** DOMContentLoaded (fires after CSS = flash)
---
## Critical Constraints
### 🚨 Rule 1: Content Padding Must Be Consistent
```css
/* ✅ CORRECT */
.layout__main {
padding: var(--space-8); /* Same for all page types */
}
/* ❌ FORBIDDEN */
.layout[data-page-type="home"] .layout__main {
padding: 0; /* Breaks alignment! */
}
```
---
### 🚨 Rule 2: Components Use Semantic Tokens Only
```css
/* ✅ CORRECT */
.card { background: var(--color-bg-elevated); }
/* ❌ FORBIDDEN */
.card { background: var(--gray-100); /* Primitive token */ }
```
---
### 🚨 Rule 3: Theme Variables on `:root` Only
```css
/* ✅ CORRECT */
:root[data-theme="dark"] { --color-bg: black; }
/* ❌ FORBIDDEN */
body[data-theme="dark"] { --color-bg: black; /* Wrong scope */ }
```
---
### 🚨 Rule 4: Only Transition Color Properties
```css
/* ✅ CORRECT */
body { transition: background-color 0.2s, color 0.2s; }
/* ❌ FORBIDDEN */
* { transition: all 0.3s; /* Layout thrashing */ }
```
---
### 🚨 Rule 5: Grid Controls Visibility
```css
/* ✅ CORRECT */
.layout[data-page-type="section"] { --col-toc: 0; }
/* ⚠️ ACCEPTABLE (but redundant) */
.layout[data-page-type="section"] .layout__sidebar--toc { display: none; }
```
---
## Minimal Template
```html
<!DOCTYPE html>
<html lang="zh-TW" data-theme="light">
<head>
<script>/* theme init */</script>
<link rel="stylesheet" href="styles.css">
</head>
<body class="layout" data-page-type="article">
<header class="layout__header">
<!-- Brand, nav, theme toggle -->
</header>
<div class="layout__body">
<aside class="layout__sidebar layout__sidebar--menu">
<nav class="sidebar__scroll"><!-- Menu --></nav>
</aside>
<main class="layout__main">
<article class="content"><!-- Content --></article>
</main>
<aside class="layout__sidebar layout__sidebar--toc">
<nav class="sidebar__scroll"><!-- TOC --></nav>
</aside>
</div>
<footer class="layout__footer"><!-- Footer --></footer>
</body>
</html>
```
---
## Essential CSS
```css
/* Variables */
:root {
--header-height: 60px;
--col-menu: 240px;
--col-toc: 240px;
--space-8: 2rem;
--gray-50: #fafafa;
--gray-950: #0a0a0a;
--color-bg-base: var(--gray-50);
--color-text-primary: var(--gray-900);
}
:root[data-theme="dark"] {
--color-bg-base: var(--gray-950);
--color-text-primary: var(--gray-100);
}
/* Layout */
.layout {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.layout__body {
flex: 1;
display: grid;
grid-template-columns: var(--col-menu, 0) 1fr var(--col-toc, 0);
}
.layout__sidebar {
position: sticky;
top: var(--header-height);
height: calc(100vh - var(--header-height));
overflow: hidden;
}
.sidebar__scroll {
height: 100%;
overflow-y: auto;
}
.layout__main {
padding: var(--space-8);
max-width: 80ch;
margin: 0 auto;
}
/* Page Types */
.layout[data-page-type="article"] { --col-menu: 240px; --col-toc: 240px; }
.layout[data-page-type="section"] { --col-menu: 240px; --col-toc: 0; }
.layout[data-page-type="home"] { --col-menu: 0; --col-toc: 0; }
```
---
## Top 5 Mistakes
**#1: Changing content padding per page**
```css
/* ❌ */ .layout[data-page-type="home"] .layout__main { padding: 0; }
/* ✅ */ .layout[data-page-type="home"] .layout__main { max-width: 100%; }
```
**#2: Using primitive tokens in components**
```css
/* ❌ */ .card { background: var(--gray-100); }
/* ✅ */ .card { background: var(--color-bg-elevated); }
```
**#3: Wrong theme variable scope**
```css
/* ❌ */ body[data-theme="dark"] { --color-bg: black; }
/* ✅ */ :root[data-theme="dark"] { --color-bg: black; }
```
**#4: JS controlling layout**
```javascript
/* ❌ */ sidebar.style.display = 'none';
/* ✅ */ body.setAttribute('data-page-type', 'home');
```
**#5: Transitioning all properties**
```css
/* ❌ */ * { transition: all 0.3s; }
/* ✅ */ body { transition: background-color 0.2s, color 0.2s; }
```
---
## Debugging
**Layout issues:**
- Check `<body data-page-type>` value
- Inspect `--col-menu` and `--col-toc` in DevTools
- Verify `.layout__main` padding unchanged
**Dark mode issues:**
- Check `<html data-theme>` value
- Verify components use `--color-*` not `--gray-*`
- Confirm theme script in `<head>` before CSS
**Sticky issues:**
- Check `position: sticky` + `top: var(--header-height)`
- Verify parent has no `overflow: hidden`
---
## Quick Actions
```javascript
// Switch page type
document.body.setAttribute('data-page-type', 'section');
// Toggle theme
function toggleTheme() {
const root = document.documentElement;
const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
root.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
}
```