install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/TerminalSkills/skills/marko" ~/.claude/skills/comeonoliver-skillshub-marko && rm -rf "$T"
manifest:
skills/TerminalSkills/skills/marko/SKILL.mdsource content
Marko — HTML-First UI Framework
You are an expert in Marko, the HTML-first UI framework by eBay that powers ebay.com. You help developers build high-performance web applications with streaming server rendering, automatic partial hydration, a concise tag-based syntax, and reactive state — optimized for fast first-paint and minimal client-side JavaScript through fine-grained reactivity and compiler optimizations.
Core Capabilities
Components
// components/product-card.marko — HTML-first syntax <let/count=0/> <div class="product-card"> <img src=input.imageUrl alt=input.name /> <h3>${input.name}</h3> <p class="price">$${input.price.toFixed(2)}</p> <div class="rating"> <for|star| of=Array.from({length: 5})> <span class=(star < input.rating ? "filled" : "empty")>★</span> </for> </div> <div class="actions"> <button onClick() { count++ }> Add to Cart ${count > 0 ? `(${count})` : ""} </button> </div> </div> style { .product-card { border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; transition: box-shadow 0.2s; } .product-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); } .filled { color: #f59e0b; } .empty { color: #d1d5db; } }
Streaming SSR
// pages/products.marko — Streaming server rendering <let/products = fetch("/api/products").then(r => r.json()) /> <html> <head> <title>Products</title> </head> <body> <h1>Our Products</h1> <!-- Streams immediately, fills in when data arrives --> <await|products| from=products> <@placeholder> <div class="skeleton-grid"> <for|_| of=Array(8)> <div class="skeleton-card" /> </for> </div> </@placeholder> <@then> <div class="product-grid"> <for|product| of=products> <product-card name=product.name price=product.price imageUrl=product.image rating=product.rating /> </for> </div> </@then> <@catch|error|> <div class="error">Failed to load products: ${error.message}</div> </@catch> </await> </body> </html>
Reactive State
// components/todo-list.marko <let/todos=[]/> <let/newTodo=""/> <form onSubmit(e) { e.preventDefault(); if (newTodo.trim()) { todos = [...todos, { id: Date.now(), text: newTodo, done: false }]; newTodo = ""; } }> <input value=newTodo onInput(e) { newTodo = e.target.value } placeholder="Add a todo..." /> <button type="submit">Add</button> </form> <ul> <for|todo, index| of=todos> <li class=(todo.done ? "done" : "")> <input type="checkbox" checked=todo.done onChange() { todos = todos.map((t, i) => i === index ? { ...t, done: !t.done } : t ); } /> <span>${todo.text}</span> </li> </for> </ul> <p>${todos.filter(t => t.done).length}/${todos.length} completed</p>
Installation
npx @marko/create my-app cd my-app && npm install npm run dev # Dev server with hot reload
Best Practices
- HTML-first — Write HTML with embedded JS, not JS that returns HTML; natural template syntax
- Streaming SSR — Use
for async data; browser gets HTML progressively, no blank page<await> - Automatic partial hydration — Marko only sends JS for interactive components; static parts stay as HTML
- Fine-grained reactivity —
creates reactive state; only changed DOM nodes update<let/> - Scoped styles — CSS in
is scoped to the component; no class name collisionsstyle {} - Tags API — Components are custom tags;
auto-resolves from<product-card>
directorycomponents/ - eBay scale — Powers ebay.com serving billions of pages/day; battle-tested for performance
- Compiler optimized — Build step optimizes component boundaries, splits server/client code automatically