ZSL

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 的狀況是:

  1. 直接叫他生成 spec 的結果肯定預期不同,因為人類很難寫的非常詳細,沒有寫的他就亂猜
  2. 後來想到叫 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);
}
```

Related Articles