Free SKILL.md scraped from GitHub. Clone the repo or copy the file directly into your Claude Code skills directory.
npx versuz@latest install vivekkarmarkar-claude-code-os-skills-port-reference-sitegit clone https://github.com/VivekKarmarkar/claude-code-os.gitcp claude-code-os/SKILL.MD ~/.claude/skills/vivekkarmarkar-claude-code-os-skills-port-reference-site/SKILL.md---
name: port-reference-site
description: Port a standalone HTML reference site into the React personal website
triggers:
- "port this reference site"
- "integrate this project"
- "port the reference website"
- when a "*reference project website/" folder exists in the repo root
---
# Port Reference Site
Port a standalone HTML project website into the React personal website. The reference site is the "old" — convert it to a React page matching the site's dark theme and integrate it.
## Worked Examples (in the personal-website repo)
Two completed ports exist. **Read both the reference and final files before starting a new port.**
### WONYP (static content — no interactive app)
- **Reference**: `wonyp reference project website/index.html`
- **Final**: `src/pages/Wonyp.jsx`
- **Pattern**: Text + architecture SVG + image slideshow + 1 YouTube embed. No iframe needed.
### Dancer Claude (interactive app + writeup)
- **Reference**: `dancer-claude reference project website/index.html`
- **Final**: `src/pages/DancerClaude.jsx` + `public/dancer-claude-app/`
- **Pattern**: Vanilla JS app iframed from `public/`, writeup sections as React JSX below it. Three YouTube embeds. Tag colors split by concept (orange=tools, yellow=techniques, blue=domain, green=audience).
Read both to understand the transformation. WONYP is the simpler case (no app). Dancer Claude is the full pattern (app + writeup).
## Before Starting
1. **Read the reference site's `index.html` completely** — identify:
- **Interactive app section** (canvas, JS modules, audio, etc.) — this CANNOT become React, it must be iframed
- **Writeup sections** (text, YouTube embeds, architecture diagrams) — these become React JSX
- **Assets** (images, audio, JSON data) — these go to `public/`
2. **Read an existing ported page for the template** — `src/pages/Wonyp.jsx` is the reference "new". Study:
- Train badge pattern
- Title + subtitle + tags
- YouTube embed pattern
- Architecture diagram (interactive SVG with clickable steps + animated particles + detail bar)
- Prose sections
- GitHub footer
## Step 1: Set Up the Iframe App
If the reference site has an interactive app (canvas, JS modules, etc.):
1. Create `public/<app-name>-app/` directory
2. Copy CSS, JS, and asset directories (NOT the videos folder unless referenced in HTML)
3. Create a **stripped-down** `index.html`:
- **Remove** any title, subtitle, or header text (the React page provides these)
- **Keep** only the interactive controls and the app itself
- Keep the `<script type="module">` tag
4. **Fix the CSS**:
- `body { background: #000000; }` — MUST match the site, not the reference site's original color
- Remove `min-height: 100vh` (it's embedded, not standalone)
5. **Verify** the app works by opening `public/<app-name>-app/index.html` directly
## Step 2: Compute Iframe Dimensions (formal-decompose)
Before writing the iframe, compute the content height from the app's CSS:
```
total_content_h = app_padding_top
+ each_section_height + each_section_margin
+ app_padding_bottom
iframe_height = total_content_h + 62px safety buffer (round up to nearest 50)
container_width = app_max_width + 2 * 80px side breathing (round up to nearest 20)
```
The iframe MUST have:
- `scrolling="no"` — no scrollbar ever
- `style={{ height: '<computed>px', background: '#000' }}`
- `className="w-full border-none block"`
- No wrapper div with borders or shadows — seamless integration
## Step 3: Create the React Page
Follow the WONYP template exactly:
```
<Layout>
<article className="w-full py-16 px-4">
<div className="max-w-2xl mx-auto prose-medium">
{/* Train badge */}
{/* Title + subtitle + tags */}
</div>
{/* Iframe — OUTSIDE the max-w-2xl container, wider */}
<div className="max-w-[<computed>px] mx-auto mb-12 px-4">
<iframe ... />
</div>
<div className="max-w-2xl mx-auto prose-medium">
{/* Writeup sections: Introduction, Architecture, etc. */}
</div>
{/* Architecture diagram — full width like WONYP */}
<ArchitectureDiagram />
<div className="max-w-2xl mx-auto prose-medium">
{/* Remaining prose + YouTube embeds + GitHub footer */}
</div>
</article>
</Layout>
```
### Tag Layout Rules
- Compute total tag width: `sum(label_chars * 8.4 + 34px) + (n-1) * 12px gap`
- Container is 672px (max-w-2xl)
- If tags overflow, split by **concept** (tools vs techniques vs domain vs audience), not arbitrarily
- Each conceptual group gets its own color row
### YouTube Embeds
Use the same pattern as WONYP:
```jsx
<div className="relative w-full rounded-xl overflow-hidden border border-neutral-800"
style={{ paddingBottom: 'min(56.25%, 540px)', boxShadow: '0 4px 32px rgba(0,0,0,0.5)' }}>
<iframe className="absolute top-0 left-0 w-full h-full border-none" src={url} ... />
</div>
```
## Step 4: Wire Up Routing
1. **`src/data/stations.js`** — Add/update station entry with `id`, `name`, `description`, `url`
2. **`src/data/lines.js`** — Update the train's stations array if the station ID changed
3. **`src/App.jsx`** — Import the page component and add the `<Route>`
## Step 5: Verify Before Pushing
1. `npm run build` — must pass
2. Use Playwright to screenshot the deployed page at 1280px width
3. Check for:
- [ ] No duplicate titles (React page title vs iframe title)
- [ ] Background matches (#000, no blue/dark-blue bleed)
- [ ] No scrollbar on iframe
- [ ] App has breathing room (not cramped)
- [ ] Tags fit their rows without orphaning
- [ ] YouTube embeds render (or at least have correct aspect ratio containers)
- [ ] Architecture diagram is interactive (clickable steps, animated particles)
- [ ] GitHub footer link is correct
## Common Mistakes to Avoid
- **DO NOT** forget the live interactive app — it's the showpiece, not the writeup
- **DO NOT** use `background: transparent` on the iframe body — use `#000` explicitly
- **DO NOT** leave the reference site's header/title in the iframe HTML
- **DO NOT** constrain the iframe to `max-w-2xl` (672px) — it needs to be wider than the app's max-width
- **DO NOT** set a fixed iframe height without computing from actual content dimensions
- **DO NOT** split tags into rows arbitrarily — split by concept if they overflow