Skip to content

Instantly share code, notes, and snippets.

@velppa
Created May 21, 2026 09:54
Show Gist options
  • Select an option

  • Save velppa/74ac56b22f32baf83f297b6f5ed79882 to your computer and use it in GitHub Desktop.

Select an option

Save velppa/74ac56b22f32baf83f297b6f5ed79882 to your computer and use it in GitHub Desktop.
name html-creator
description Create a standalone HTML page on a user-requested topic, upload it as a private GitHub gist via the local Emacs `gist-files` function, and open it through gisthost.github.io in the browser. Use when the user asks to "make an HTML page", "build a webpage about X", "share an HTML demo", or otherwise wants a one-off self-contained HTML artifact published as a gist.
compatibility Requires a running Emacs server with the `gist` package loaded and a working GitHub auth token for the gist API. Browser launching uses `browse-url`.
allowed-tools Bash(emacsclient:*), Write
metadata
author version
velppa
1.0.0

Overview

Generate one self-contained HTML file on the topic the user specified, save it to a temp path, then publish it as a private GitHub gist via Emacs. After upload, open the rendered page through the gisthost proxy so the user sees the live result.

End-to-end flow:

  1. Author HTML at a temp path (/tmp/<slug>.html).
  2. Call gist-files through emacsclient --eval with private=t and a callback that browses to https://gisthost.github.io/?<gist-id>.
  3. Report the gisthost URL to the user.

HTML authoring rules

  • Single file. No external CSS/JS files. Inline <style> and <script>.
  • CDN links allowed when topic needs a library (D3, Chart.js, Tailwind via CDN, etc.). Prefer pinned versions.
  • <!DOCTYPE html>, <html lang="en">, <meta charset="utf-8">, <meta name="viewport" content="width=device-width, initial-scale=1">.
  • Sensible <title> based on topic.
  • Default styling readable on mobile + desktop. Dark-mode friendly via prefers-color-scheme when not contradicted by user request.
  • No tracking, no analytics, no external fonts unless user asked.
  • Pure static. No build step.
  • File name: short kebab-case slug derived from topic, .html extension.

Strip local-only links

Source material (org-mode notes, Markdown, internal wikis) often carries links that resolve only on the author's machine. Published HTML must not contain any of them — gist viewers, mobile browsers, and other people's machines cannot follow them. Remove or rewrite:

  • file:// URLs.
  • Relative paths: ./foo.org, ../notes/bar.md, ~/Notes/..., bare Notes/....
  • Org-mode internal targets: [[id:...]], [[*Heading]], [[#custom-id]], [[file:...]].
  • Markdown footnote-style refs that point at undefined local anchors.
  • localhost, 127.0.0.1, private IP ranges (10.x, 192.168.x, 172.16–31.x).
  • emacsclient:, org-protocol:, x-callback-url:, or other scheme handlers only registered locally.
  • Intranet hostnames (no public DNS) — heuristic: single-label host or .local, .lan, .internal, company-private TLDs.

Rewrite rule: if the link text is meaningful, keep the text and drop the <a> wrapper. If the link was the only signal, drop it entirely. Never invent a public URL to replace a local one. Same goes for <img src>, <link>, <script src> — only https:// URLs survive.

Design intent

Goal: turn a document the user would skim into one they would actually read. Pick an archetype, then commit to the affordances that archetype implies. Prefer spatial layout over prose dumps. Use SVG for diagrams, inline <script> for small interactions (≤ ~30 lines), CSS variables for theming.

Style defaults when user did not specify:

  • System font stack. No web fonts.
  • Max content width ~72ch for prose, full-bleed for dashboards.
  • prefers-color-scheme light/dark variants via CSS variables.
  • Color-code semantics: ok/warn/error, severity, diff add/remove. Pick 3–5 accent colors, define as variables.
  • Generous whitespace, sticky in-page nav when content > one screen.
  • Copy-to-clipboard buttons on code blocks and exportable artifacts.

JS rules of thumb:

  • No framework unless user asked. Vanilla DOM is fine for small UIs.
  • Reach for libraries (via CDN, pinned) only when topic demands them:
    • D3 / Chart.js for non-trivial charts.
    • Mermaid for flowcharts when ad-hoc SVG would be tedious.
    • Tailwind via CDN if user wants a polished marketing-style page.
  • Arrow-key navigation, drag-and-drop, sliders, tabs, collapsibles all doable in under 50 lines — prefer those over heavier deps.

Artifact archetypes

Match user's topic to an archetype, then bias the output toward its default affordances:

Archetype Affordances
Explainer / concept teardown TL;DR box, collapsible sections, inline glossary on hover, SVG diagrams
Code review / PR summary Inline diff with margin annotations, severity-tagged findings, jump links, color-coded changes
Module map / architecture Boxes-and-arrows SVG, highlighted hot path, click-to-expand node details
Slide deck Section-per-slide, arrow-key + space navigation, slide counter, full-screen mode
Design system / palette Swatch grid, copy-able tokens, contrast badge
Post-mortem Timeline, log excerpts in <pre>, follow-up checklist
Status report / dashboard KPI cards, embedded chart, last-updated stamp
Triage board Drag-and-drop columns, markdown export button
Feature-flag panel Grouped toggles, dependency warnings, diff-copy button
Prompt tuner / playground Split panel, live variable re-render, copy-prompt button
Animation sandbox Isolated transition, duration/easing sliders
Single-file mini-app index.html mindset: no deps, sparse styling, do one thing

Prompt patterns that work

Two prompt shapes seen in the wild that produce good HTML output — use these as templates when the user gives only a topic and no structural hints:

  • Rich explainer:

    "Explain {topic} in detail. Reformat it, expand confusing bits, go deep into how it works. Output HTML, neatly styled, using HTML/CSS/JavaScript to make the explanation rich, interactive, and as clear as possible."

  • Single-file mini-app:

    "In a single index.html, no dependencies, sparse styling, create an app that {does X}."

If the user's request is vague, ask one short clarifying question about archetype (explainer vs. mini-app vs. report) before generating — a wrong-archetype draft wastes more time than a 5-second check.

Upload + open

Call gist-files with one-element filename list, private=t, callback that extracts gist id and browses gisthost:

emacsclient --eval "$(cat <<'EOF'
(gist-files
 (list "/tmp/<slug>.html")
 t
 (lambda (gist)
   (let ((id (oref gist :id)))
     (browse-url (format "https://gisthost.github.io/?%s" id))
     (kill-new (format "https://gisthost.github.io/?%s" id))
     (message "html-creator: %s" id))))
EOF
)"

Notes:

  • gist-files signature: (filenames &optional private callback). Pass t for private.
  • Callback receives a gh-gist-gist object. Use (oref gist :id) for the gist id, (oref gist :html-url) for the raw gist page if needed.
  • gisthost.github.io renders the gist's HTML file directly, so the URL form is https://gisthost.github.io/?<id> — query string is the bare id, no path.
  • Call is async inside Emacs; emacsclient returns before the gist exists. The callback handles browse-url once the API responds.

Reporting back to user

After firing the elisp call, tell the user:

  • Topic / title of the HTML page.
  • Local path of the temp file.
  • The gisthost URL pattern (https://gisthost.github.io/?<id>) — exact id arrives once Emacs's async callback runs; the user's browser will pop open with it. The id is also copied to kill-ring by the callback.

If the user wants the canonical gist page instead of the rendered preview, give them https://gist.github.com/<user>/<id> or (oref gist :html-url).

Quoting tips

  • Use the heredoc form (emacsclient --eval "$(cat <<'EOF' ... EOF)") so the lambda body keeps its double quotes intact.
  • Avoid embedding the HTML body inside the elisp form — always go through a file path. gist-files reads the file itself.
  • Temp path must not contain shell metacharacters; stick to /tmp/<slug>.html with kebab-case slug.

Failure modes

  • If emacsclient fails, ask user to M-x server-start.
  • *ERROR*: (void-function gist-files) → package not loaded. Ask user to (require 'gist).
  • Auth failure → Emacs will prompt for a GitHub token in its own minibuffer; tell the user to check the Emacs frame.
  • browse-url opens a non-default browser → not this skill's concern; browse-url-browser-function is user-configured.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment