{"slug": "artifactkit-a-ui-kit-for-ai-agents-that-write-html", "title": "Artifactkit – A UI kit for AI agents that write HTML", "summary": "Artifactkit is a zero-dependency UI kit that lets AI agents produce reports, dashboards, kanbans, wikis, scorecards and decks as a single self-contained .html file that opens by double-click with no server, build or network. The kit ships two validators that fail the build when an artifact breaks its content rules, targeting recurring agent errors such as plotting a partial period beside closed ones, inventing values where the source had none, and exporting raw datasets instead of filtered rows. The project cites canoncss measurements across 130 generations showing unconstrained output agrees with itself on 58–67% of its styling vocabulary, while a written specification lifts agreement to roughly 90%.", "body_md": "**A UI kit for AI agents that write HTML.** Reports, dashboards, kanbans, wikis,\nscorecards and decks, as one self-contained `.html` file that opens by\ndouble-click with no server, no build and no network.\n\nZero dependencies in the core. No third-party code in this repository.\n\nI ask an agent for a dashboard or a report most weeks. Every time I get different conventions, a CDN link that dies the moment the file leaves my laptop, and a chart that quietly plots a half-finished month beside closed ones. Correcting the same five things on every artifact is not a workflow. So the corrections live here instead: one vocabulary, a set of content rules, and two validators that fail the build when an artifact breaks them.\n\nAgents get the same things wrong:\n\n- a partial period plotted beside closed ones, drawing a cliff that reads as a collapse\n- an invented value where the source had nothing, instead of `–` and a reason\n- CSV export dumping the raw dataset, not the rows you filtered to\n- a sorted table that never says which column is sorted\n- six colours on one measure, encoding nothing\n\nNone of that is a styling problem, which is why the CSS is the smallest part of\nthis. [canoncss measured it](https://github.com/marcelodevelop/canoncss) across 130 generations: unconstrained output\nagrees with itself on 58–67% of its styling vocabulary, a written specification\nlifts that to ~90%, and a strict prompt over plain Tailwind matched a closed\nvocabulary. The specification does the work.\n\nCopy one and change the data. Each is a single file with no dependencies.\n\nA quarterly review. Action title that states the finding, a KPI strip carrying a\nconcentration risk rather than only totals, a banner naming the excluded partial\nperiod, and one margin shown as `–` because the source did not deliver it.\n\nFilter chips, search, sparklines in cells, sticky first column. Row detail is a drilldown, not a modal, because a modal hides what you were comparing against. KPIs derive from the filtered set so they cannot drift from the screen, and the view state lives in the URL so a filtered view is shareable.\n\nNative drag between columns, counts and KPIs recomputed on drop, blocked cards carrying the blocking reason. Save writes the board back into the file itself through an allow-listed serializer.\n\n[**Demo**](https://vespassassina.github.io/artifactkit/deck.html) ·\n[audience build](https://vespassassina.github.io/artifactkit/deck-audience.html) ·\n[source](/vespassassina/artifactkit/blob/main/examples/templates/deck.html)\n\n`N` opens a presenter window: notes, pace against a stated budget, and\nnext-up, on your screen only. `O` overview, `?` keys,\n`P` one slide per page. The audience build has the notes physically\nstripped, not hidden, because hiding still ships the text.\n\nDecisions kept separate from open questions. Contents built from the document so\nit cannot drift, search on `/`, and rejected alternatives recorded\nrather than forgotten.\n\nWeighted dimensions with the rubric stated in full, because a scorecard without its rubric is an opinion in a grid. Reviewer spread above a threshold is flagged rather than averaged away.\n\nWeights fixed before scoring, \"do nothing\" kept in as the honest baseline, and what each option costs to be wrong. A gate row shows why the highest-scoring option is still not the recommendation: a weighted score ranks the options that remain viable, it does not decide which ones are.\n\nEvery component on one page. If it looks wrong here it is wrong everywhere.\n\n| **Offline** | Save any page, disconnect, reload. No CDN links, no fonts to fetch, nothing to break. | \n| **Print** | `Ctrl` /`Cmd` +`P` . Chrome disappears, exhibits stay whole, collapsed sections expand, SVG charts stay sharp, notes never appear. | \n| **Export** | Filter the dashboard, then Export CSV. You get the rows on screen, in the order you sorted them. | \n| **Retheme** | Find `ak-theme-start` . Set`--t-bg:#14141A; --t-ink:#E8E6E0; --t-lift-amt:7%` for a dark build. | \n\nEdit the constants. Everything else derives through `color-mix`, so fills and\nrules invert on their own when the ground goes dark.\n\n```\n/*!ak-theme-start*/\n:root{\n  --t-bg:#F1ECE1;  --t-ink:#1A1A18;  --t-accent:#14496B;\n  --t-density:1;   --t-size:15px;    --t-maxw:1120px;\n}\n/*!ak-theme-end*/\n```\n\nThe ground is RAL 9010 `#F1ECE1`, converted from the CIELAB definition\n(93.613, −0.425, 6.008). RAL publishes no normative hex and the third-party\ncharts disagree from `#EFEEE5` to `#FFFFFF`. It is a warm off-white. Pure white\nis wrong.\n\nSystem fonts only, so it works offline and does not look like everything else.\n\n| Tables | sort with a visible indicator, filter, CSV of the *filtered* rows | \n| Charts | bar, line, donut, sparkline. Hand-rolled SVG, prints sharp, ships a data twin for screen readers | \n| Kanban | native HTML5 drag, wrapped once so you never write `dragover` handling | \n| Decks | presenter window, pace timer, overview, print per slide | \n| Saving | File System Access with a download fallback, dirty tracking, no false \"saved\" | \n|  | `window.print()` . 0 kB against ~180 kB for jsPDF plus html2canvas, and better output | \n\nPlus the long tail whose absence is what makes agent output diverge: stepper, timeline, disclosure, drilldown, KPI strip, scorecard, breadcrumb, TOC, banner, empty state, freshness chip, provenance footer.\n\nPublished at both discovery paths, so it works in Claude Code, Copilot, Cursor and Codex:\n\n```\n.agents/skills/artifactkit/     Copilot · Cursor · Codex\n.claude/skills/artifactkit/     Claude Code\n```\n\n[SKILL.md](/vespassassina/artifactkit/blob/main/.agents/skills/artifactkit/SKILL.md) has the gates, build procedure,\ncontent rules and gotchas.\n[RECIPES.md](/vespassassina/artifactkit/blob/main/.agents/skills/artifactkit/references/RECIPES.md) has worked\npatterns per artifact type.\n[MODULES.md](/vespassassina/artifactkit/blob/main/.agents/skills/artifactkit/references/MODULES.md) covers optional\nlibraries and what to avoid.\n[EVAL-RESULTS.md](/vespassassina/artifactkit/blob/main/.agents/skills/artifactkit/references/EVAL-RESULTS.md) is the\nmeasured trigger rate: 0.90, zero false fires, across three model families.\n\nNothing is vendored here. `--with` fetches at build time and inlines the library\nplus its licence text:\n\n```\nnode scripts/build.mjs report.html -o out.html --with marked,prism\n```\n\n`marked`, `prism`, `minisearch`, `papaparse`, `sortablejs`, `chartjs`, `reveal`.\nAll MIT/ISC/BSD, all UMD. Network is needed at build time only, and if it is\nunreachable the build fails loudly rather than shipping something half-broken.\n\nMeasured in Chromium, because the documentation on this is thin and often wrong:\n\n- `file://` is a secure context, so File System Access and WebCrypto work\n- every `file://` page shares one localStorage area, so keys must be namespaced\nby artifact id or unrelated artifacts read and clobber each other\n- ES modules are CORS-blocked, classic scripts load, so everything must be UMD\n- cross-origin `fetch` works only against APIs that send CORS headers\n- OPFS is blocked\n- OAuth from `file://` is impossible. Entra needs an https or localhost redirect\nURI and an`Origin` header, and`file://` sends`null` . Fetch the data at build\ntime and inline it\n\nDesktop Windows and macOS. No mobile, no touch.\n\n```\nnpm run build && npm run check          # no install step, there are no dependencies\n\nnode scripts/build.mjs in.html -o out.html --id slug [--with marked] [--strip-notes]\nnode scripts/validate.mjs out.html     # self-containment, credentials, accessibility\nnode scripts/drift.mjs out.html        # structural integrity\nnode scripts/sync-skill.mjs            # after editing src/\nnode scripts/shots.mjs                 # regenerate the screenshots\n```\n\nTwo checkers because one is not enough: a rule checker sees vocabulary, not\nerosion. Strip every takeaway and source line from a report and `validate.mjs`\npasses it with zero warnings while `drift.mjs` fails it four ways.\n\nContributing notes, including two Windows traps that have already bitten, are in\n[CONTRIBUTING.md](/vespassassina/artifactkit/blob/main/CONTRIBUTING.md).\n\nMIT. Libraries fetched with `--with` keep their own, inlined into each artifact\nautomatically.", "url": "https://wpnews.pro/news/artifactkit-a-ui-kit-for-ai-agents-that-write-html", "canonical_source": "https://github.com/vespassassina/artifactkit", "published_at": "2026-09-11 13:52:58+00:00", "updated_at": "2026-09-11 14:16:17.689532+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["Artifactkit", "canoncss", "Tailwind"], "alternates": {"html": "https://wpnews.pro/news/artifactkit-a-ui-kit-for-ai-agents-that-write-html", "markdown": "https://wpnews.pro/news/artifactkit-a-ui-kit-for-ai-agents-that-write-html.md", "text": "https://wpnews.pro/news/artifactkit-a-ui-kit-for-ai-agents-that-write-html.txt", "jsonld": "https://wpnews.pro/news/artifactkit-a-ui-kit-for-ai-agents-that-write-html.jsonld"}}