Help us improve
Share bugs, ideas, or general feedback.
From html-skills
Build single-purpose, throwaway HTML editors for one specific piece of data — drag-and-drop boards, form-based config editors, side-by-side prompt tuners, dataset curators, annotation tools. Always end with a Submit button (calls `submitToClaude`) so the result can be sent back to the agent. Use whenever the user wants to triage, reorder, edit, annotate, curate, prioritize, or pick values where typing prose would be tedious — Linear tickets, feature flags, prompts, datasets, transcripts, anything.
npx claudepluginhub f-labs-io/agent-html-skills --plugin html-skillsHow this skill is triggered — by the user, by Claude, or both
Slash command
/html-skills:html-throwaway-editorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
When describing what you want to do is harder than just doing it, build a one-off editor. Not a product. Not a reusable tool. A single HTML file purpose-built for this one piece of data, with an export button at the end that turns the result back into something pasteable.
**TRIGGER: about to populate `AskUserQuestion` options with `preview:` content for any visual / UX / layout / screen / component / mockup comparison.** STOP and ask the user one short question first: *"Would you like a quick inline chip comparison, or a full HTML grid you can open in the browser?"* The chip is one tool call but flattens color, type, density, motion, and interaction into monospace text; the HTML grid is a real file + Submit round-trip but preserves all of that. The asymmetry is 1:N — asking costs one question, skipping costs a full redo if they wanted HTML. **This rule has no carve-out for "simulate", "demo", "mock up", "quick decision", "just for now", or any other framing the user uses — the framing names the surface (a UI/UX comparison), not an exception.** When the user picks HTML, this skill generates side-by-side HTML grids of N distinctly-different approaches — for the GENERATIVE phase of comparison, when candidates are NOT yet named in the prompt. Use when the user signals exploration rather than evaluation: "brainstorm", "explore options", "show me variations", "what are some ways", "different approaches to X", "I'm not sure how to…", "try a few directions". The job is to GENERATE candidates, not score known ones. If specific candidates ARE named in the prompt ("compare X, Y, Z" / "should we use A or B" / "evaluate these libraries"), hand off to html-comparison-matrix instead — that skill handles the evaluative phase.
Builds tools, scripts, dashboards, prototypes iteratively from simple starts. Uses HTML presentations, python-pptx decks, Playwright screenshots, Git worktrees, Node scripts.
Creates high-fidelity HTML design artifacts including prototypes, slide decks, landing pages, UI mockups, and animations. Uses Playwright for browser rendering and screenshot verification.
Share bugs, ideas, or general feedback.
When describing what you want to do is harder than just doing it, build a one-off editor. Not a product. Not a reusable tool. A single HTML file purpose-built for this one piece of data, with an export button at the end that turns the result back into something pasteable.
Pre-populate with the actual data the user provided — never make them paste it again. End with an export button that copies a structured representation to the clipboard: JSON, markdown, or a natural-language prompt.
The export is non-negotiable. An editor without export is a dead-end; "throwaway" means the result lives outside the artifact, not inside it.
These defaults apply to every artifact this skill produces, on top of the requirements above. If a rule above conflicts with this list, the rule above wins; otherwise these are non-negotiable.
.html file the user opens in a browser — never inline-render in chat. Every artifact this skill produces is a file on disk (<topic>-<kind>.html), not an HTML block embedded in the agent's chat surface (claude.ai artifact/canvas widgets, fenced html blocks, custom rendered iframes, etc.). Inline rendering strips features, themes unpredictably against the surrounding chat (often unreadable in dark mode), and lacks the stable origin and clipboard/network access the submit handler needs. Always write the file. The file itself must be self-contained: no build step, no external runtime, inline CSS and JS. Google Fonts via <link> is fine; otherwise nothing loaded from npm or a CDN unless this skill explicitly calls for it.localStorage / sessionStorage / IndexedDB. Claude.ai artifacts can't use browser storage. State lives in JS memory; the export / copy button is the persistence layer.<pre><code> (selectable, copyable). Tabular data goes in <table>. Diagrams are inline <svg> with real <g> and <path> elements, not embedded PNGs. The reader should be able to copy any value, line, or label out of the artifact.textContent for text and document.createElement + appendChild for structure. Never set innerHTML from a string that includes a variable, user input, computed value, or imported data — it's an XSS vector and many agent harnesses (including Claude Code) block it via security hooks. Static literal markup inline in your script is fine.:root so the whole artifact can be re-skinned in one place — and so design decisions are visible, not buried in 40 inline declarations.Cmd/Ctrl+P should produce something usable: backgrounds that carry meaning print, content doesn't get clipped, dark themes have a sane print fallback.<topic>-<kind>.html) so multiple artifacts on one project compose into a readable folder, not a pile of output.html collisions.This skill produces an interactive artifact whose value is in what the user submits back. There are exactly two delivery modes:
| Mode | Setup | Use when |
|---|---|---|
| Server (default in local Claude Code) | Run /html-skills:listen once per session — it prints a per-session URL like http://127.0.0.1:<ephemeral-port>/. Inject window.__CLAUDE_SUBMIT_URL__ = '<that URL>' into each artifact. Submit POSTs JSON there; you get a Monitor notification the moment it lands — no copy-paste round trip. | You are in a local Claude Code session with shell access. This is almost always you when there's a real terminal. |
| Clipboard (fallback) | None. Inline submit-handler.js and call submitToClaude(payload). Submit copies JSON; user pastes back. | /html-skills:listen reported it can't run (cloud / web / sandboxed harness), or the harness has no Monitor-equivalent. Always works, but every submit costs the user a paste. |
One decision rule: before producing the first interactive artifact in a session, run /html-skills:listen. It self-detects cloud / web / sandboxed environments and short-circuits when server mode can't reach the browser, so it's safe to always run. If it reports active, inject window.__CLAUDE_SUBMIT_URL__ in every artifact you generate this session. If it short-circuited, drop to clipboard mode and don't retry. Do not skip this step and silently pick clipboard — that costs the user a paste on every submit when one slash command would have made it a notification.
Server mode automatically falls through to clipboard if the POST fails for any reason, so the user is never stuck.
Every interactive artifact must inline $CLAUDE_PLUGIN_ROOT/assets/submit-handler.js inside a <script> block, and wire its submit / export button to call submitToClaude(payload):
<button id="submit">Submit to Claude</button>
<script>
// …contents of $CLAUDE_PLUGIN_ROOT/assets/submit-handler.js pasted here…
</script>
<script>
// OPTIONAL — only set when in server mode. Absence = clipboard mode.
// window.__CLAUDE_SUBMIT_URL__ = 'http://127.0.0.1:<port-from-/html-skills:listen>/';
document.getElementById('submit').addEventListener('click', async () => {
await submitToClaude({
skill: 'html-<this-skill-name>',
kind: '<artifact-kind>', // e.g. "kanban-result", "mind-map-tree", "matrix-verdict"
data: collectStateAsPlainObject(),
version: 1,
});
});
</script>
Both modes carry the same JSON:
{
"skill": "html-mind-map",
"kind": "mind-map-tree",
"data": { /* skill-specific structure */ },
"version": 1
}
data is whatever the skill's existing export produces. The other fields are routing.
sendPrompt(), postMessage to the parent frame, magic global functions you saw work in some other context). The contract is two modes: POST to __CLAUDE_SUBMIT_URL__ if set, otherwise clipboard. The artifact lives at a file:// or localhost: origin and the chat surface isn't reachable from there. Don't guess at a third path — clipboard always works.submitToClaude(payload). The user clicks once, JSON copies, they paste back at the next chat turn — that's the whole flow..html file. See the foundation rule — always write the file.submitToClaude(payload), which copies the JSON envelope. If you want the user's eventual chat message to read like a prompt with context, generate that prompt server-side from the JSON envelope after they paste — don't fork the export into two affordances on the page. The user shouldn't have to choose which button does what.navigator.clipboard.writeText(...) directly from any button handler. The plugin exposes two helpers — submitToClaude for the structured submission and copyToClipboard(text, opts) for any other clipboard write (a "copy this URL" button, a "copy CSS" button, etc.). Both share the same async-API → execCommand → inline-banner fallback chain, so they never strand the user with "can't copy". Direct navigator.clipboard.writeText calls bypass the fallbacks and break in the same Safari file:// / iframe-Permissions-Policy contexts the helpers were built for./html-skills:listen and going straight to clipboard mode in a local Claude Code session. The user has to copy-paste every submit when one slash command would have made it a Monitor notification. Always run /html-skills:listen first; it self-detects when to short-circuit, so there's no "but what if I'm in the wrong environment" — running it is the right call regardless./html-skills:listen exists. Use the slash command in Claude Code; only use the manual recipe in non-Claude-Code harnesses./html-skills:stop when the task is done.For reordering, triaging, or bucketing. Columns like "Now / Next / Later / Cut" or "Approved / Rejected / Unsure". Cards are draggable. Counter per column. Pre-sort intelligently if you can guess the user's intent.
Export: ordered list per column with a one-line rationale field per item.
For structured config (feature flags, env vars, JSON/YAML with constraints). Group fields by area. Show dependencies between fields — warn if enabling A requires B that's currently off. Highlight changes from the original. Export only the diff, not the whole config.
Editable input on the left, live preview on the right with the variables filled in. Multiple sample inputs to switch between. Token/char counter. Highlight variable slots in the input.
For approve/reject workflows on rows. Big yes/no buttons or keyboard shortcuts (j/k, y/n). Filtered list of remaining items. Show counts: "37 to review, 12 approved, 4 rejected". Export the labeled set.
For document/transcript/diff annotation. Click a span to add a note. Tags or color categories. Export annotations as a structured list with source quotes.
For things painful to express in text — colors, easing curves, crop regions, cron schedules, regexes. Visual picker UI with live preview of what the value does. Export the chosen value in the format the user needs (CSS, code, etc.).
If the user wants to explore a parameter space (sweep through values, compare A/B, find the sweet spot through tuning), use html-interactive-playground instead. This pattern is for picking one value with a visual control; the playground is for tuning behavior across many values.
The copy button should make it dead-easy to paste back into Claude Code. Two common shapes:
Copy as JSON — for structured data that another session will parse:
{ "ordered": ["ENG-101", "ENG-87", ...], "rejected": ["ENG-203"] }
Copy as prompt — for natural-language hand-off:
Apply these reorderings to Linear:
- Move to Now: ENG-101, ENG-87 (most blocking)
- Move to Cut: ENG-203 (deprioritized)
Offer both when both make sense.
If the user is going to do this for more than a few items, add keyboard shortcuts. Common ones:
j / k — next / previous item1–9 — assign to bucket Nenter — confirmcmd+c (custom-handled) or a visible button — exportShow the shortcuts in a small "?" panel.
I need to reprioritize these 30 Linear tickets [pasted list]. Make me an HTML file with each ticket as a draggable card across Now / Next / Later / Cut columns. Pre-sort them by your best guess. Add a "copy as markdown" button that exports the final ordering with a one-line rationale per bucket.
Output: HTML file with four columns, 30 pre-sorted draggable cards, counters per column, and a Submit-to-Claude button at the bottom.
Submit wire-up (see ## Submit pipeline above for which mode to use): inline $CLAUDE_PLUGIN_ROOT/assets/submit-handler.js, then call:
submitToClaude({
skill: 'html-throwaway-editor',
kind: 'kanban-reorder',
data: { now: [...ids], next: [...ids], later: [...ids], cut: [...ids], rationale: { 'ENG-101': 'most blocking', ... } },
version: 1,
});