---
name: "html_answer_cards"
description: "Turn an agent's answer into rich, styled HTML cards: safe rendering of model-generated content (escape + sanitize), reusable card templates, responsive grids, and dark-mode theming with CSS tokens. Triggers on 'answer in HTML', 'render as a card', 'HTML answer', 'answer card template', or when agent output needs to be shown as styled HTML."
---

# HTML Answer Cards

## Purpose

Make agent answers look good as HTML without shipping XSS. The pattern: structured answer data goes through a fixed card template, every model-generated string is escaped or sanitized before it reaches the DOM, and one token-based stylesheet handles light/dark mode and responsiveness. The result is a self-contained HTML answer a chat surface, page, or email client can drop in as-is.

## Workflow

1. **Separate data from markup.** The agent produces plain answer data (title, kicker, summary, bullet list, key/value facts, links), never raw HTML. The card template in `assets/card.html` owns the markup; the agent only fills the fields. If the answer needs rich text, the pipeline is: markdown -> sanitize -> insert. Never raw `innerHTML` of model text.
2. **Sanitize before rendering.** Treat every model string as untrusted user input (prompt injection is the new XSS). Server-side: escape with `html.escape` (Python) or template auto-escaping; never mark model content `safe`. Client-side: run the markdown-converted HTML through DOMPurify with explicit allow-lists, configured AFTER conversion, not before (see `references/sanitization.md`). Links get a `https?/mailto` URI allow-list; kill `javascript:`, event handlers, and `style` attributes.
3. **Build the card from the template.** Copy `assets/card.html`. Use the semantic slots: `<article class="card">` with `.card-kicker`, `.card-title`, `.card-summary`, `.card-facts` (a `<dl>`), `.card-points` (a `<ul>`), `.card-actions` (links). Do not invent new class names; new visual needs go through modifiers (`.card--accent`, `.card--compact`).
4. **Theme with tokens, not hardcoded colors.** All color comes from `assets/cards.css` custom properties (`--card-bg`, `--card-text`, `--card-muted`, `--card-line`, `--card-accent`). Dark mode is a single override block keyed to `prefers-color-scheme: dark` plus an optional `[data-theme]` manual override. Set `color-scheme: light dark` so native controls follow the theme.
5. **Make it responsive from zero.** Base styles are single-column, mobile-first. Multi-card layouts use `repeat(auto-fill, minmax(min(280px, 100%), 1fr))`, which adapts with no breakpoints. Cards reused in sidebars or panels can opt into container queries (`container-type: inline-size`) so they respond to their box, not the viewport.
6. **Validate the output.** Run the checks in `references/qa-checklist.md`: no horizontal scroll at 320px, light and dark both readable (4.5:1 text contrast), no raw `<script>`/`on*` in the final HTML, keyboard focus visible on all links, `prefers-reduced-motion` respected.

## Output Contract

An agent applying this skill produces a self-contained HTML answer:

- One document (or embeddable fragment) using `assets/card.html` structure and `assets/cards.css` tokens, with no external CSS/JS/fonts unless the host page already provides them.
- All model-generated strings escaped server-side or DOMPurify-cleaned client-side; final HTML contains no `javascript:` URIs, event-handler attributes, or `<script>`/`<style>` elements from model content.
- Readable in light and dark mode, no horizontal scroll down to 320px wide, semantic landmarks (`article`, headings, `dl`/`ul` where they fit).
- For multi-card answers: a `.card-grid` wrapper; every card is self-contained so cards can be reordered, filtered, or rendered individually.

## Operating Rules

- The template owns markup; the agent owns data. Never concatenate model strings into HTML by hand. This is the rule that prevents XSS, and it has no exceptions.
- DOMPurify defaults are a floor, not a policy. Always pass explicit `ALLOWED_TAGS`, `ALLOWED_ATTR`, and `ALLOWED_URI_REGEXP`; tighten the URI list to what the answer actually needs and re-audit it whenever the library bumps (see references for the rationale and a verified config).
- `style` attributes and `<style>` tags from model content are always stripped. If a card needs custom styling, add a named modifier class to the template; never honor inline styles from the model.
- No layout that assumes a fixed width. Cards must survive a 320px phone, a narrow chat pane, and full-bleed desktop without edits.
- Keep the CSS token list short and stable. If a new answer needs a color that is not a token, question the answer first; most of the time it maps to an existing token.
- Respect the reader: honor `prefers-reduced-motion`, keep link text descriptive (never "click here"), and give every image `alt` text or `alt=""` when decorative.
