# Editorial Portfolio

> Generate a Claude-designed personal portfolio website in editorial bilingual (中/英) dark-mode style. Single-page React app with hero / methodology / timeline / awards / press / inputs / agent chat / contact sections. Trigger when the user asks to build their personal website / portfolio / 个人网站, or wants a Claude-quality "looks-like-Peng-Zhiwei's-site". Reads user context (chat, attached docs, GitHub, prior bio) and produces a deployable codebase, ideally one-shot.

- Skill: `wilson534/editorial-portfolio` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add wilson534/editorial-portfolio`
- Raw SKILL.md: https://api.skillmd.com/api/skills/wilson534/editorial-portfolio/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: wilson534 (https://skillmd.com/u/wilson534)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/wilson534/editorial-portfolio

---


# Editorial Portfolio

A static React SPA personal portfolio in editorial bilingual style — dark mode, serif headings, mono accents, custom cursor ring (desktop), mobile bottom action bar, AI chat agent, scroll-driven reveals. Designed for fresh-grad / IC / founder profiles. **Zero build step** — Babel-standalone in the browser. **Deploys to Vercel in 30 seconds.**

Reference live site (built by 彭智炜 / @wilson534): https://pengzhiwei.vercel.app

## When to use

- User asks "帮我做个人网站 / build my portfolio / 给我做个像彭智炜那样的站"
- User has a resume / GitHub profile / LinkedIn dump and wants a polished personal site
- User wants something deployable to Vercel today, not a Next.js project that takes a week

## What you do

End-to-end ownership: codebase generation → local preview → deploy. The user should not have to learn React, CSS, or Vercel.

### Step 1 — Gather information from context

Mine the conversation history, attached files, GitHub repo, prior resume for these fields. Most map 1:1 to `data.js`:

| Field | Where to find it | data.js key |
|---|---|---|
| Name (Zh + En) | first message, signature, GitHub | `profile.nameZh / nameEn` |
| Role | job title or self-described | `profile.role / roleEn` |
| Tagline | one-liner of what they ship | `profile.tagline` |
| Statement | 1-sentence philosophy | `profile.statement` |
| Location | city or "X → Y" if relocating | `profile.location` |
| Status | hiring / open-to-work / busy state | `profile.status` |
| Photo | portrait image path or URL | `profile.photo` (drop into `assets/portrait.png`) |
| Contact links | GitHub / 小红书 / Email / WeChat / Linkedin etc. | `profile.links` array |
| Hero figures | 3-4 numbers + labels (e.g. "12 awards · 7 hackathons") | `profile.figures` |
| Internships | dated work entries | `timeline` items with `kind: "internship"` |
| Projects | independent shipped projects | `timeline` items with `kind: "project"` |
| Hackathons | competition / showcase entries | `timeline` items with `kind: "hackathon"` or `"showcase"` |
| Awards | competition wins, scholarships | `awards` array |
| Press | interviews, articles, videos | `press` array |
| Skills | grouped chips: tools / stack / methods / domain | `skills` array of `{group, items}` |
| Inputs | what they're listening / reading / using right now | `inputs` |

**If a section is genuinely missing from context, omit it cleanly:**
- For top-level sections (press, inputs, agent chat) you can drop the entire `<section>` block in `app.jsx`
- For timeline, just have fewer entries — the section auto-handles empty kinds

**If critical fields are missing** (name, no work history at all), ask **once** in a single bundled message.

### Step 2 — Lay down the template

Copy the entire `templates/` directory to a new working directory (e.g. user's `~/portfolio/` or wherever they want). The structure is:

```
portfolio/
├── index.html             # root HTML — loads React + Babel from CDN
├── styles.css             # all design tokens + responsive layout
├── app.jsx                # React app (no build step needed)
├── tweaks-panel.jsx       # dev-only edit-mode panel (safe to leave)
├── data.js                # ← THE FILE YOU EDIT
├── .vercelignore          # excludes dev artifacts from deploy
└── assets/
    └── portrait.png       # ← REPLACE with user's photo
```

### Step 3 — Edit `data.js` only (DO NOT touch CSS or JSX)

`data.js` is the single source of truth for content. Replace every field under `window.PZW_DATA = { ... }` with the user's data. The existing values are 彭智炜's — use them as **format reference** only, then overwrite.

The structure with example shape:

```js
window.PZW_DATA = {
  profile: {
    nameZh: "彭智炜",                    // → user's Chinese name
    nameEn: "Musketeer",                // → user's English name / nickname
    role: "技术型 AI 产品工程师",        // → user's role
    roleEn: "Technical AI Product Engineer",
    tagline: "...",                     // one-liner
    statement: "...",                   // 1-2 sentence philosophy
    location: "Shanghai → Beijing",
    status: "summer 2026 · 开放合作 / 实习",
    photo: "assets/portrait.png",
    links: [ { label, handle, href }, ... ],
    figures: [ { label, value }, ... ]
  },
  timeline: [ { kind, date, year, org, title, ... }, ... ],
  awards: [ { year, name, prize, sub }, ... ],
  press: [ { outlet, title, sub, kind, href }, ... ],
  skills: [ { group, items: [...] }, ... ],
  inputs: { listening: [...], reading: [...], using: [...] },
};
```

### Step 4 — Replace placeholders in `app.jsx`

A few strings outside data.js use `{{...}}` placeholders. Replace these directly in `app.jsx`:

| Placeholder | Replace with |
|---|---|
| `{{NAME_ZH}}` | user's Chinese name (multiple occurrences) |
| `{{NAME_EN}}` | user's English name |
| `{{FIRST_NAME_ZH}}` | first name only (e.g. "智炜" → use last 2 chars of nameZh) |
| `{{GITHUB_USERNAME}}` | GitHub handle (no `@`) |
| `{{YEAR}}` | current year for footer copyright (e.g. 2026) |

Search-and-replace with `sed -i '' 's/{{NAME_ZH}}/...//g'` style.

### Step 5 — Drop in the portrait photo

Replace `assets/portrait.png` with the user's photo. Recommended:
- portrait orientation (taller than wide)
- crop ratio approximately 3:4
- 1000px+ on the long edge

If the user hasn't provided one, leave a note in the README and skip — site will display a broken image but everything else works.

### Step 6 — Local preview

Start a static server in the working directory and tell the user the URL:

```bash
cd portfolio/
python3 -m http.server 5173
# → open http://localhost:5173
```

Verify:
- Hero loads, name/role/tagline correct
- Timeline shows correct entries
- Mobile viewport (375px) works — no horizontal scroll, bottom action bar visible
- Chat agent responds (or doesn't crash if no API set)

### Step 7 — Deploy to Vercel

```bash
cd portfolio/
vercel link --yes --project <user-handle>     # one-time link, e.g. "wilson534"
vercel --prod --yes                           # deploy
```

Output: `https://<user-handle>.vercel.app`

If user doesn't have Vercel CLI: `npm i -g vercel && vercel login`.

## Style rules baked in (don't alter)

- **Editorial dark mode** (default): `bg #0d0c0a` + `fg #ede7dc` + accent `oklch(0.85 0.16 118)` lime
- Three direction modes user can toggle: `editorial / paper / terminal` (the tweaks panel exposes this)
- Bilingual: Chinese for sections + 大字, English for tech terms + small caps + mono labels
- Single-column mobile, max 1100px breakpoint folds nav into FAB stack
- Custom cursor ring on desktop, native cursor on touch
- `SUMMER 2026` style chip in hero — auto-update season per user's launch context

## Customization

Most users won't touch CSS. If they want to:
- **Accent color**: `--accent-hue` in `:root` (try 30 = orange, 280 = violet)
- **Direction default**: change `tw.direction` initial in app.jsx
- **Font**: `--serif` and `--mono` in `:root`

## Files

- `templates/` — full deployable static site, drop in and go
- `templates/data.js` — single content source, contains 彭智炜's data as format reference
- `templates/assets/` — placeholder portrait. Replace before deploy.

## Companion skill

Pair with [`editorial-resume`](https://github.com/wilson534/editorial-resume) — generates the matching single-page A4 PDF resume that the portfolio's mobile RESUME button links to.

