Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,24 @@
"security",
"compliance"
]
},
{
"name": "design-from-code",
"source": "./plugins/design-from-code",
"description": "Design UI changes from real source code: trace handler->query->schema to confirm what each number counts, iterate faithful AS-IS/TO-BE HTML mockups (keep/new/changed tags), lock a design doc, then delegate. Claude Code plugin & Codex skill.",
"version": "1.0.0",
"author": {
"name": "nlook"
},
"category": "Design UX",
"homepage": "https://github.com/nlook-service/design-from-code",
"keywords": [
"design",
"ui",
"mockup",
"workflow",
"html"
]
}
]
}
}
19 changes: 19 additions & 0 deletions plugins/design-from-code/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "design-from-code",
"version": "1.0.0",
"description": "Workflow skill that designs UI changes from real source code and data models — verify data in code, iterate faithful HTML mockups, lock a design doc, then delegate implementation. It reproduces existing components pixel-for-pixel by reading actual JSX/queries instead of imagining them.",
"author": {
"name": "nlook",
"url": "https://nlook.me"
},
"homepage": "https://github.com/nlook-service/design-from-code",
"license": "MIT",
"keywords": [
"design",
"ui",
"mockup",
"workflow",
"html",
"skill"
]
}
62 changes: 62 additions & 0 deletions plugins/design-from-code/skills/design-from-code/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
name: design-from-code
description: Workflow skill that turns one issue/requirement into a design in the order "verify real code & data → iterate faithful HTML mockups → design doc (.md) → delegate implementation." When designing a change to existing UI, it reads the actual component source and data model instead of imagining them, and reproduces the current screen pixel-for-pixel. Triggers (EN) "design this", "make a mockup", "how should this feature look", "add ~ to the existing screen"; (KO) "설계해줘", "시안 만들어줘", "이 기능 어떻게 보여줄지", "기존 화면에 ~ 추가".
---

# design-from-code — design mockups grounded in real source

> One line: **lock facts with code → agree via HTML → nail it in a `.md` → ship by delegating.**
> The key difference: mockups are not *imagined*. They are reproduced down to the pixel and the number by **reading the real component JSX and data queries.**

## When to use

- Designing a change that **adds or modifies something** in an existing screen/component (e.g. "add a metrics card to the bottom bar")
- **UI re-layout / emphasis** requests like "it's hard to see / I want it shown like this"
- Exposing data (numbers, stats) on screen when you need to **pin down exactly what each number counts**
- When the user says "mockup first / show me in HTML"

## When NOT to use

- Pure backend/CLI work, changes with no UI
- A simple, already-agreed bug fix (no mockup needed)

## Core principles (these override every other decision)

1. **No guessing — read the code.** "How does this stat work?" is wrong ~100% of the time if you imagine it. Trace handler→query→schema all the way down, confirm it as *fact*, then design. → `references/data-model-verification.md`
2. **Reproduce the real component faithfully.** Before any new mockup, draw the **current state (AS-IS) exactly as the real JSX renders it**. Schematic drawings cause misunderstandings. → `references/code-fidelity-reproduction.md` (★ the heart of this skill)
3. **Pictures over prose.** Iterate with self-contained HTML you can see and fix. Bump the version (v2, v3…) on every round of feedback. → `references/html-mockup-recipe.md`
4. **Confirm one decision at a time.** "A vs B?" → "7-day window?" → "monotone color?" — ask narrowly.
5. **Mark keep / new / changed.** When "leave the rest as-is" is a requirement, use 🟦keep / 🟩new / 🟨changed color tags to show what you are *not* touching.
6. **Empty / initial state is first-class.** Always design the data-zero (new user) screen alongside the populated one.
7. **Delegate the build, but verification is mandatory.** Close it out with build / type-check / tests.

## The 8-step workflow

| # | Step | Key tools | Detail |
|---|------|-----------|--------|
| 1 | Read the issue verbatim | `Bash` + `gh issue view` | Don't open GitHub via WebFetch (auth fails) |
| 2 | Map the code | `Agent(Explore)` ×N | Get just the conclusions for related components/hooks/schema |
| 3 | **Verify the data** | `Bash` (grep/sed) + Explore | handler→use-case→repository→schema. `references/data-model-verification.md` |
| 4 | HTML mockup v1 | `Write` (.html) + `SendUserFile` | Phone frame, 2–3 options, per-state, inline SVG. `references/html-mockup-recipe.md` |
| 5 | User confirmation | reply / `AskUserQuestion` | Confirm one at a time; v2, v3 per feedback |
| 6 | **Faithful AS-IS/TO-BE** | `Bash` (read JSX with sed) + `Write` | Extract real render fns/classes/labels → HTML. `references/code-fidelity-reproduction.md` |
| 7 | Design doc `.md` | `Write` | Approved mockup + verified data model + build entry points + phases |
| 8 | Delegate + verify | `Agent` (language expert) + direct | Delegate with the contract & verify commands baked in; close with build/tests |

## Reference files

- `references/code-fidelity-reproduction.md` — **★ concrete technique for reading the real component with sed and reproducing it 1:1 in HTML** (worked `renderSeg` example)
- `references/data-model-verification.md` — how to trace the data model all the way down (events-table username-attribution example)
- `references/html-mockup-recipe.md` — recipe for self-contained HTML (phone frame, inline-SVG charts, design tokens, empty state)
- `references/prompt-templates.md` — copy-paste prompts for each step
- `examples/issue-976-walkthrough.md` — the full flow of a real case (v1→v5, including the point where the user corrected "top/bottom is reversed")

## Common mistakes (checklist)

- [ ] Wrote data behavior from a **guess** → did you confirm it in code?
- [ ] Mockup is **schematic** so it differs from reality → did you read the real JSX and reproduce classes & labels?
- [ ] Asked several decisions at once and caused confusion → did you confirm one at a time?
- [ ] "Keep existing" was the requirement but it's unclear what stays untouched → did you add keep/new/changed tags?
- [ ] Forgot the empty / initial state (data-zero) → did you include the empty-state design?
- [ ] Pulled in a chart library for the graph → did you use inline SVG instead?
- [ ] Skipped build/type-check after implementing → did you close it out with verification commands?
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Worked Example — Issue #976 (Mobile Bottom Brand Status)

> The complete flow of applying this skill to a real issue. Recorded as-is, including why the mockup went from v1 through v5 and the exact points where the user corrected course.

## Requirement (Issue, original text)
> "On the mobile bottom, instead of content that's hard to see, show information (numbers) that appeal psychologically, and make them big. Posts published, visitors, likes. A weekly graph showing the rate of change. Add a branding element."

Target = the bottom sheet `MobileBottomBar`.

---

## Application, Step by Step

### [1] Read the issue
`gh issue view 976 ...` → restated intent: "Big numbers in the bottom bar (published / visitors / likes) + a weekly rate-of-change graph + branding."

### [2] Map the code (Explore ×1)
→ peek = nav icons + the + FAB; full = secondary menu, recent posts, calendar, brand metrics (the bottom 3 tiles), and footer. Found the reusable component `Sparkline.tsx`. Data comes from `fetchSummary` (currently days=1).

### [3] Verify the data (direct sed + Explore)
Traced handler → use case → repository → schema. **Confirmed facts**:
- `events` has no author_id → **attribution is by the `username` string**.
- `WHERE username=$1 AND device_type<>'bot' AND is_self=false`.
- If there's no `doc`, it's **the home page plus all posts, summed**. A `daily[]` time series exists.
- Likes live only in `likes` and there's no aggregation API → **a new endpoint is needed**.
- There's no per-day time series for likes → decided to disable the graph tab.
→ This verification let us answer the user's question "Do visitors include both home and posts?" **with evidence from the code**.

### [4]–[6] HTML mockup iterations (v1→v5) — ★user feedback drove the direction
| Version | Change | Trigger |
|---------|--------|---------|
| v1 | Compared two options: option A (hero) and option B (3 numbers) | First presentation |
| v2 | Locked in option B + data-model panel + added an **empty state** | "Like option B / visitors not available yet" |
| v3 | Introduced AS-IS vs TO-BE (keep / new / changed tags) | "Re-propose by combining existing + new on top of the current bar" |
| v4 | **Read the actual JSX** to reproduce faithfully (renderSeg, the + FAB circle, the 5-cell grid, the date calendar) | "More accurate, please" |
| v5 | Corrected to **pin the menu at the top / status below the menu** | "The menu is the bottom bar, so it goes at the very top, with the proposal below it" |

→ **Lesson**: faithful code reproduction in v4 (§code-fidelity) sharply raised accuracy, and in v5 the user corrected the top/bottom placement. Because we locked things down narrowly, one at a time, it converged fast.

### [7] Design document
`docs/02-design/issue-976-mobile-bottom-brand-status-design.md` — the locked-in v5 + the verified data model + implementation starting points + P1/P2/P3.

### [8] Implementation + verification
- Backend (delegated to a backend specialist): `GET /<resource>/likes-summary?days=N` → `{total,current,previous}`. go build + 33 tests PASS.
- Frontend (done directly): days=1→14, the likes query, the status card (first child of the body, empty state), absorbing the existing metrics module. build 0 errors, net-new tsc 0 errors.
- Menu and existing modules left untouched.

---

## Meta Lessons Taken From This Case

1. **Data verification builds user trust.** Answering "Do visitors include both home and posts?" from the code let the design proceed without snags.
2. **Faithful reproduction is the key to mockup accuracy.** The "this is accurate" reaction came at v3 (schematic) → v4 (code reproduction).
3. **Spatial constraints like placement get lost in words.** A diagram + locking things down one at a time fixed it by v5.
4. **Empty states from the start.** With real visitors at 0, the empty state was actually the top-priority screen.
5. **Playwright is a supporting tool.** It all failed due to environment issues, but reading the source carefully was enough.
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Faithfully Reproducing Real Components (Code-Fidelity Reproduction)

> The heart of this skill. Accuracy "matching the current design" comes from **reading the actual source code, not from Playwright captures**.
> Goal: make the mockup HTML **look identical to the real component — down to the icons, labels, spacing, and colors**.

---

## 0. Why read the code (not Playwright)

- A screenshot only gives you "the visible result." **Class names, conditional rendering, per-state branches, and exact labels** live only in the code.
- Live-app captures (Playwright) depend on the environment (server boot, login, viewport), so they break often. In fact, on this task Playwright failed entirely with timeouts, and the **pixel-matching mockup was built from source reading alone**.
- Conclusion: **Priority #1 = reading the source.** A real Playwright capture is a supplementary aid for *verification/comparison* (nice to have, but optional).

---

## 1. Procedure (5 steps)

### STEP 1 — Find the component file
```bash
find app/src -iname "*BrandStudio*" # candidate files
grep -rln "하단바\|BottomBar\|StudioBar" app/src
```

### STEP 2 — Grasp the render function / structural skeleton
A component usually draws repeated UI through small `renderXxx` helpers. Find these first.
```bash
grep -n "renderSeg\|renderProfile\|leftSegs\|rightSegs\|ModuleCard\|return (" \
app/src/components/mobile/MobileBottomBar.tsx | head
```
→ You get a skeleton like "peek is `leftSegs.map(renderSeg)` + +FAB + `rightSegs.map(renderSeg)`."

### STEP 3 — Read the render function body closely (★core)
Use `sed` to read the function body verbatim and extract its **Tailwind classes, icons, labels, and conditional styles**.
```bash
sed -n '/const renderSeg/,/^ );$/p' \
app/src/components/mobile/MobileBottomBar.tsx
```
What this actually yields:
```tsx
const renderSeg = (s) => (
<button className={cn(
'flex flex-1 flex-col items-center gap-0.5 rounded-xl py-1.5 text-[10px]',
s.active ? 'font-bold text-foreground' : 'font-medium text-muted-foreground' // ← active = weight, not color
)}>
<s.icon className="h-5 w-5" strokeWidth={s.active ? 2.5 : 2} /> // ← icon size/weight
<span className="truncate">{s.label}</span>
</button>
);
```
→ **Facts read off**: vertical layout (icon on top, label below), icon `h-5 w-5`, text `text-[10px]`, **no background/color — only active gets `font-bold text-foreground`** (monotone), rounded `rounded-xl`.

### STEP 4 — Confirm exact labels, icons, and defaults
If labels are i18n, get the actual strings from the locale; if they're slot defaults, confirm them in the constants.
```bash
grep -n "DEFAULT_PEEK_SLOTS\|EDITABLE_KEYS" .../MobileBottomBar.tsx
sed -n '52,60p' app/src/locales/ko/bottomBar.json # actual labels
```
→ peek defaults = `글·캘린더·+·통계·프로필` (Posts · Calendar · + · Stats · Profile), module titles = "최근 글" (Recent posts) · "이번 주" (This week), footer = `ABOUT|PRIVACY|TERMS`.
**Never invent labels here.** Use only the actual strings.

### STEP 5 — Translate 1:1 into HTML/CSS
Map Tailwind classes to CSS of the same meaning. Use the **class → CSS mapping cheat sheet** (§2 below).
```html
<!-- renderSeg → HTML -->
<div class="seg on"><svg .../>글</div>
<style>
.seg{flex:1;display:flex;flex-direction:column;align-items:center;gap:2px;
border-radius:12px;padding:5px 0;font-size:10px;font-weight:500;color:var(--muted-foreground)}
.seg.on{font-weight:800;color:var(--foreground)} /* active = weight, not color — straight from the code */
.seg svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:2}
</style>
```

---

## 2. Tailwind → CSS mapping cheat sheet

| Tailwind | CSS |
|---|---|
| `flex flex-col items-center` | `display:flex;flex-direction:column;align-items:center` |
| `gap-0.5` / `gap-2` | `gap:2px` / `gap:8px` (×4px) |
| `h-5 w-5` / `h-12 w-12` | `height/width:20px` / `48px` (×4px) |
| `text-[10px]` / `text-sm` / `text-xl` | `font-size:10px` / `14px` / `20px` |
| `font-medium/bold/extrabold` | `font-weight:500/700/800` |
| `rounded-xl` / `rounded-2xl` / `rounded-full` | `border-radius:12px / 16px / 50%` |
| `p-3` / `px-4 py-1.5` | `padding:12px` / `padding:6px 16px` |
| `bg-primary text-primary-foreground` | `background:var(--primary);color:#fff` |
| `text-foreground` / `text-muted-foreground` | `color:var(--foreground)` / `var(--muted-foreground)` |
| `border border-border` | `border:1px solid var(--border)` |
| `bg-muted/60` | `background:rgba(244,244,245,.6)` (theme muted + alpha) |
| `tabular-nums` | `font-variant-numeric:tabular-nums` |
| `shadow-md` | `box-shadow:0 4px 10px rgba(0,0,0,.15)` |

> For design tokens (`--foreground`, `--primary`, etc.), check the real values in the project's theme CSS and put approximations into `:root`. If the project is monotone, keep it monotone.

---

## 3. Handling icons

- The real component typically uses an icon library (lucide, etc.). In the mockup, **a simple inline SVG** that mimics the same silhouette is enough.
- The key is to match the **size, weight, and monotone-or-not** exactly as read from the code (`h-5 w-5` → `width:20px`, `strokeWidth=2`).
```html
<svg viewBox="0 0 24 24" style="width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:2">
<path d="M4 20h16M6 16l9-9 3 3-9 9H6z"/> <!-- pen (post) icon approximation -->
</svg>
```

---

## 4. AS-IS / TO-BE two-column layout

- **AS-IS** = the *current state as-is*, reproduced via STEP 1–5.
- **TO-BE** = clone AS-IS, then swap **only the parts that change**. Prove with code that everything else is left untouched.
- Tag each block with a color: 🟦 unchanged (no `outline`) / 🟩 new (`outline:2px solid #86efac`) / 🟨 changed (`outline:2px solid #fcd34d`).
- **Placement constraints** are also drawn as a separate diagram box (top→bottom stack) to reach agreement. (e.g., "the menu is pinned to the top of the sheet, new items go below it" — on this task the user corrected this top/bottom ordering.)

---

## 5. Common mistakes

- ❌ Inventing labels ("대시보드" (Dashboard), "내 정보" (My info)) → ✅ Only the actual i18n strings.
- ❌ Painting the active state in color (blue) → ✅ If the code says `font-bold text-foreground`, use **weight/brightness** (preserve monotone).
- ❌ Making the + button a rounded square → ✅ If the code says `rounded-full`, make it **circular**.
- ❌ Reordering modules arbitrarily → ✅ Keep the order as it appears in the JSX (Recent posts → This week → metrics → footer).
- ❌ Dropping conditional UI (e.g., the music-player row) → ✅ Reflect branches like `musicActive &&` in the mockup, at least as a comment.

---

## 6. (Optional) Verify against a real Playwright capture

Overlaying your source-built mockup **against an actual app screenshot** is the surest validation. But it's heavily environment-dependent.
```bash
# After booting the dev server, capture the real component in a mobile viewport → pixel-compare against the mockup
```
- On this task, Playwright failed entirely — `file://` blocking, frame detached, timeouts — so **source reading alone was sufficient**.
- In other words, Playwright is **a supplement when available, skippable when not**. Priority #1 is always reading the source.
Loading