VibesOS design
Self-contained design transformer — invoke directly, do not decompose. Transforms a design reference HTML file into a Vibes app. Use when user provides a design.html, mockup, or static prototype to match exactly.
git clone https://github.com/popmechanic/VibesOS
T=$(mktemp -d) && git clone --depth=1 https://github.com/popmechanic/VibesOS "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/design" ~/.claude/skills/popmechanic-vibesos-design && rm -rf "$T"
skills/design/SKILL.mdPlan mode: If you are planning work, this entire skill is ONE plan step: "Invoke /vibes:design". Do not decompose the steps below into separate plan tasks.
Display this ASCII art immediately when starting:
░▒▓███████▓▒░░▒▓████████▓▒░░▒▓███████▓▒░▒▓█▓▒░░▒▓██████▓▒░░▒▓███████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓█▓▒░▒▓█▓▒▒▓███▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░░▒▓████████▓▒░▒▓███████▓▒░░▒▓█▓▒░░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░
Design Reference Transformer
Transform a complete design reference HTML file into a working Vibes app with TinyBase reactive data.
Core Principle
Preserve and adapt, don't interpret and recreate.
The design reference is source code to transform, not inspiration to interpret. When given a complete HTML file with styles, your job is to make minimal surgical changes to connect it to React/TinyBase—not to recreate it from your understanding of its aesthetic.
When to Use This Skill
Use this skill when:
- User provides a
, mockup, or static prototype filedesign.html - User says "match this design exactly" or "use this as a reference"
- User wants their existing HTML converted to a Vibes app
- A previous implementation didn't match the design reference
The Transformation is Mechanical
The conversion from design HTML to React/TinyBase is deterministic, not creative:
| Transformation | Rule | Example |
|---|---|---|
| Attributes | → | → |
| Attributes | → | → |
| Attributes | kebab-case → camelCase | → |
| Self-closing | Add explicit close | → |
| Comments | HTML → JSX | → |
| Inline styles | String → Object | → |
| Event handlers | Lowercase → camelCase | → |
CSS requires NO changes. Copy the entire
<style> block verbatim.
Workflow
Step 1: Read the Design Reference
# Read the design file completely Read design.html
Note the structure:
block (copy verbatim)<style>- HTML structure (preserve exactly)
- Any vanilla JavaScript (will be replaced with React)
Step 2: Identify Dynamic Content
Ask: "What content comes from the database?"
Typical dynamic elements:
- List items that repeat (
).map() - Text that users enter (controlled inputs)
- Counts, totals, timestamps
- User-specific content
Everything else stays static.
Step 3: Create the React Component
function App() { // TinyBase hooks are globals — no imports, no initialization call needed const rowIds = useRowIds('items'); const handleAdd = useAddRowCallback('items', (e) => ({ text: '', type: 'item', created: Date.now() })); return ( <> {/* CSS copied VERBATIM from design.html */} <style>{` /* Paste entire <style> block here unchanged */ `}</style> {/* HTML structure preserved, only syntax converted */} {/* Dynamic content replaced with {expressions} */} </> ); }
Step 4: Handle Dark Mode Override (If Needed)
The Vibes template has dark mode support. If your design is light-only, add this CSS override:
/* Force light theme regardless of system preference */ html, body, #container, #container > div { background-color: var(--your-bg-color) !important; }
Note: Avoid targeting
[style*="position: fixed"] as this will style the VibesSwitch toggle button.
Step 4b: Scope CSS to Avoid VibesSwitch/VibesPanel Conflicts
The template includes a VibesSwitch toggle button and VibesPanel admin menu that sit outside your app container. Broad CSS selectors can accidentally style these components.
Watch for these problematic patterns:
| Problematic | Why | Safe Alternative |
|---|---|---|
| Styles VibesSwitch toggle | or |
(with colors/backgrounds) | Cascades everywhere | Scope to specific containers |
| Targets VibesSwitch | Target by class/ID instead |
| May match menu wrapper | Use |
If your design has global button/element styles:
- Wrap your app content in a container with a class:
<div className="app">...</div> - Scope broad rules:
→button { }.app button { } - Or use
which is the template's app root#container
The template already protects components with:
button[aria-controls="hidden-menu"] { background: transparent !important; } #hidden-menu { /* menu-specific variable resets */ }
But defense-in-depth is better—scope your CSS to avoid conflicts.
Step 5: Assemble and Test
VIBES_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$(dirname "${CLAUDE_SKILL_DIR}")")}" bun "$VIBES_ROOT/scripts/assemble.js" app.jsx index.html
Open in browser and visually diff against the design reference. They should be pixel-identical except for dynamic content.
Anti-Patterns (DO NOT DO THESE)
| Anti-Pattern | Why It's Wrong | Correct Approach |
|---|---|---|
| Translate colors to OKLCH | Changes the design | Use exact hex values from reference |
| Restructure HTML "for React" | Breaks layout | Preserve structure, only change syntax |
| "Improve" the CSS | Not your job | Copy verbatim |
| Add your own classes | Introduces drift | Use exact classes from reference |
| Interpret the "vibe" | Creates divergence | Be literal, not interpretive |
| Skip vanilla JS analysis | Miss functionality | Understand what it does, then React-ify |
Transformation Checklist
Before writing code, verify:
- Read the entire design.html file
- Identified all
blocks (will copy verbatim)<style> - Identified dynamic content (lists, inputs, user data)
- Identified vanilla JS functionality (will convert to React)
- Noted any custom fonts (add to imports if needed)
- Checked for dark/light theme assumptions
During transformation:
- CSS pasted unchanged (no "improvements")
- HTML structure preserved exactly
- Only syntax converted (class→className, etc.)
- Dynamic content uses
and{expressions}.map() - Vanilla JS replaced with React hooks and handlers
- Dark mode override added if design is light-only
After assembly:
- Visual comparison with design reference
- All interactive elements work
- Data persists on refresh
- No console errors
- VibesSwitch toggle (bottom-right) displays correctly with no background box
- VibesPanel menu opens when toggle is clicked
- Menu buttons are correctly styled (not inheriting app button styles)
Example: Static List → Dynamic List
Design HTML:
<ul class="item-list"> <li class="item">First item</li> <li class="item">Second item</li> </ul>
React with TinyBase:
const rowIds = useRowIds('items'); <ul className="item-list"> {rowIds.map(id => ( <ItemRow key={id} id={id} /> ))} </ul> // Child component uses useCell for reactive per-row data function ItemRow({ id }) { const text = useCell('items', id, 'text'); return <li className="item">{text}</li>; }
Note: Only the content changed. The classes, structure, and styling are identical.
Example: Static Form → Controlled Form
Design HTML:
<form> <input type="text" class="input" placeholder="Enter text..."> <button class="btn">Submit</button> </form>
React with TinyBase:
const [text, setText] = useState(''); const handleAdd = useAddRowCallback('items', () => ({ text, type: 'item', created: Date.now() })); <form onSubmit={(e) => { e.preventDefault(); handleAdd(); setText(''); }}> <input type="text" className="input" placeholder="Enter text..." value={text} onChange={(e) => setText(e.target.value)} /> <button type="submit" className="btn">Submit</button> </form>
Note: Same structure, same classes, same placeholder. Only added React bindings.
Integration with Vibes Assembly
This skill produces an
app.jsx that works with the standard Vibes assembly:
# In the working directory VIBES_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$(dirname "${CLAUDE_SKILL_DIR}")")}" bun "$VIBES_ROOT/scripts/assemble.js" app.jsx index.html
The assembly script:
- Inserts your JSX into the Vibes template
- Handles OIDC authentication wrapper (via Pocket ID)
- Sets up import maps for React and TinyBase
- Configures sync URLs if present (auth is automatic via Pocket ID)
What's Next?
After transforming a design reference, present these options using AskUserQuestion:
Question: "Design reference transformed! What's next?" Header: "Next" Options: - Label: "Test locally" Description: "Open index.html in browser to verify it matches the design exactly" - Label: "Deploy to Cloudflare (/cloudflare)" Description: "Push the app live to Cloudflare Workers" - Label: "Make adjustments" Description: "Fine-tune specific elements while preserving the design" - Label: "I'm done" Description: "Wrap up - files are saved locally"