html-artifact-qa · v0.1.0 · 2026-10-08 · sha256 73bffd604b2fa548

html-artifact-qa v0.1.0B

Immutable. This exact content never changes and is served at /api/v1/blob/73bffd604b2fa548.

---
name: html-artifact-qa
description: "Use when a user asks a complex, multi-part, or deeply technical question that benefits from interactive HTML output: visualizations, collapsible sections, live code, diagrams. Do NOT use for simple factual questions, short code completions, or conversational replies."
license: MIT
compatibility: gptme (webui with artifact rendering)
metadata:
  author: gptme-contrib
  version: "0.1.0"
  tags:
    - ux
    - artifacts
    - html
    - qa
    - interactive
  requires_tools: []
  requires_skills: []
keywords:
  - "explain with html"
  - "interactive visualization"
  - "complex question html"
  - "show me how it works"
  - "visualize this"
  - "answer with interactive"
  - "how does X work with diagram"
  - "explain visually"
---

# HTML Artifact Q&A Skill

Respond to complex technical questions with rich, interactive HTML artifacts
rendered natively in gptme's webui.

## When to use

Fire this skill when the question has **two or more** of these complexity signals:

- **Multi-part**: asks "how/why/what" about a system with multiple components
- **Visual benefit**: the answer is clearer with a diagram, table, or chart
- **Technical depth**: involves algorithms, protocols, distributed systems, data
  structures, or debugging multi-layer stacks
- **Long question**: >3 sentences or multiple nested sub-questions

Simple lookups, one-liner code completions, and conversational replies do NOT
warrant an HTML artifact — prefer plain text for those.

## Rendering path

gptme renders `html` codeblocks as first-class artifacts via `artifacts_api.py`
(`RENDERABLE_KINDS` includes `"html"`). No extra tool calls needed: output an
HTML codeblock and the webui surfaces it as an iframe-previewed artifact.

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Answer: [topic — see Output safety]</title>
  <style>
    /* minimal accessible CSS — dark-mode aware */
    body { font-family: system-ui, sans-serif; max-width: 820px; margin: 2rem auto; padding: 0 1rem; line-height: 1.6; color-scheme: light dark; }
    details summary { cursor: pointer; font-weight: 600; }
    pre { overflow-x: auto; background: #f4f4f4; padding: 1rem; border-radius: 4px; }
    @media (prefers-color-scheme: dark) { pre { background: #222; } }
    table { border-collapse: collapse; width: 100%; }
    th, td { border: 1px solid #ccc; padding: .5rem; text-align: left; }
  </style>
</head>
<body>
  <h1>[Topic — the OUTPUT of one escape-routine pass on raw user text]</h1>
  <p>[1-2 sentence TL;DR — your own wording; if it quotes the user, embed the routine's OUTPUT, never the raw quote]</p>

  <details open>
    <summary>Section 1: [key concept]</summary>
    <p>...</p>
  </details>

  <details>
    <summary>Section 2: [next concept]</summary>
    <p>...</p>
  </details>

  <!-- Add diagrams as inline SVG, tables for comparisons, or <pre><code> for examples -->
</body>
</html>
```

## Layout guidelines

- **TL;DR first**: a 1-2 sentence plain-language summary before the details.
- **`<details>` sections**: each major concept gets its own collapsible.
- **Comparisons → tables**: use `<table>` for side-by-side comparisons or option
  lists.
- **Code examples → `<pre><code>`**: use plain `<pre><code>` blocks; no external
  syntax highlighter. CSS `color: inherit` and a contrasting background are
  enough for readability without an external library.
- **Diagrams → inline SVG**: small ASCII-to-SVG or hand-crafted SVG for flow/
  state diagrams. No external chart library needed for simple diagrams.
- **Self-contained**: no external dependencies. The artifact must render
  offline-first — no CDN script tags, no remote fetch.

## Output safety: escape user-derived text

The question topic and any quoted user content are user-controlled strings.
**HTML-escape them before embedding** — a question like
`explain <img src=x onerror=alert(1)>` must render as text, not execute. Use one
of these two mechanisms, both shown in the template:

1. **Write user-derived text into the markup only as the routine's OUTPUT** —
   run the RAW text through this routine exactly once, then embed what it
   returns:
   `s.replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]))`
   Entities in text nodes render as the original characters, so the display is
   correct and no script is involved. Example: raw input `<img src=x>` →
   embed `&lt;img src=x&gt;`.
2. **No user text in JavaScript, ever**: no user-derived content inside
   `<script>` blocks, JS string literals, or event-handler attributes —
   `</script>` in the input would break out of the script element. Do not
   "un-escape" escaped markup by reading it back with `textContent` — entities
   would show literally.

**Exactly one routine pass per piece of raw text — never zero, never two.**
Every `&lt;` `&amp;` sequence you write into the artifact must be the result of
escaping one piece of RAW user text. A concrete example of correct OUTPUT:
raw input `<img src=x>` → embed `&lt;img src=x&gt;`. Running already-escaped
text through the routine again double-escapes it (`&lt;` → `&amp;lt;`) and
renders entities visibly; writing raw `<` from user input directly into
markup is XSS.

Treat the artifact as untrusted input rendered in an iframe on the user's
machine: never interpolate raw user text into `<title>`, `<h1>`, attribute
values, or inline `<script>`.

## After the artifact

Follow the HTML codeblock with a one-paragraph plain-text summary for users
who skim or use the terminal interface. Do not repeat the full content — just
name the sections and where the key insight lives.

## Related

- gptme artifact infrastructure: `gptme/server/artifacts_api.py`
- `ArtifactDescriptor` (kind `iframe` / `live_app`): `gptme/message.py`
- Skill origin: `tasks/html-artifact-qa-skill.md` (idea #5780)