plugins/acss-kit/skills/components-html/SKILL.md
Use when the user asks for static HTML component snippets (plus optional vanilla JS) for fpkit-style acss-kit components in non-React projects.
npx skillsauth add shawn-sandy/acss-plugins components-htmlInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Generate static HTML versions of fpkit-style components for projects that don't use React — server-rendered apps, static sites, design-system docs, email templates, prototypes.
This skill is a sibling to the components skill (which generates TSX). Both read the same reference docs at references/components/<name>.md. The TSX generator copies the ## TSX Template block; this generator copies ## HTML Template and (for stateful components) ## Vanilla JS. The ## SCSS Template block is identical for both — the SCSS is framework-agnostic.
Emit:
<name>.html — pure HTML markup. Same classes, same data-* attributes, same ARIA as the TSX output. The fragment is meant to be pasted into a page or template — no surrounding <html>/<body>.<name>.scss — byte-identical to what /kit-add generates. Compile via Sass or rename to .css and inline plain CSS.<name>.js — for components with runtime behavior. Currently: Button (aria-disabled wrap), Card (interactive-variant keyboard activation), Alert (dismiss + auto-hide + pause-on-hover), Dialog (showModal + backdrop close), Popover, Checkbox, Input, IconButton. Tiny ES module that wires behavior via the shared _stateful.js helper where applicable. Stateless components (Img, Link, Icon, List, Table, Field, Nav, plus the non-interactive Card variant) emit no .js file..scss to .css and inline the variables that don't have a fallback. Most CSS variables in the templates already have hardcoded fallbacks, so vanilla CSS works for the majority of components.Run this check at the start of every /kit-add-html invocation.
Run python3 ${CLAUDE_PLUGIN_ROOT}/scripts/detect_html_target.py <project_root> to read or initialize .acss-html-target.json.
If the script returns "source": "configured", use the reported componentsHtmlDir. Skip the prompt.
If the script returns "source": "none", ask:
Where should HTML components be generated? (default: components/html)
After the developer answers (or accepts the default), write .acss-html-target.json at the project root:
{ "componentsHtmlDir": "components/html" }
Commit this file — /kit-add-html reads it on subsequent runs.
Remember the answer for the current session as well, so subsequent calls don't re-read the file unnecessarily.
Check if _stateful.js exists in <componentsHtmlDir>.
If not found:
${CLAUDE_PLUGIN_ROOT}/assets/html-foundation/_stateful.js into <componentsHtmlDir>/_stateful.js.Created _stateful.js (foundation helper — required by stateful components).This file is the vanilla-JS counterpart to React's inlined useDisabledState. Stateful components import wireDisabled from ./_stateful.js.
Read the reference doc at ${CLAUDE_PLUGIN_ROOT}/skills/components/references/components/<name>.md (the same path the TSX generator uses). The catalog at references/components/catalog.md lists every available component plus its HTML status.
If the component is not catalogued, inform the developer and run /kit-list (which lists all available components, regardless of output format).
A reference doc that supports HTML output contains:
## Generation Contract — export_name, file, scss, dependencies. Reuse this verbatim — the dependency tree is the same for HTML and TSX.## HTML Template — fenced html block. Copy verbatim into <name>.html.## SCSS Template — fenced scss block. Copy verbatim into <name>.scss. (Identical to the TSX generator.)## Vanilla JS — fenced js block. Only present on stateful components. Copy verbatim into <name>.js.## Accessibility — read it. Don't strip ARIA attributes or focus styles out of the templates during generation; they are load-bearing.If a reference doc is missing ## HTML Template, fall back gracefully: warn the developer, offer to author the markup from the TSX template by hand, but do not silently skip. The catalog HTML Status column tracks which components have been augmented.
Walk dependencies recursively using each dependency's own Generation Contract. Build the full list of files that will be created. Identical to the TSX generator's algorithm — see ${CLAUDE_PLUGIN_ROOT}/skills/components/SKILL.md Step B3.
Example for Dialog (which depends on Button only — no IconButton, no Icon — per the current Dialog generation contract):
dialog.html + dialog.scss + dialog.js
→ button.html + button.scss + button.js
Before generating any files, display:
Generating the following files in components/html/:
New:
_stateful.js (foundation helper — vanilla JS)
button.html + button.scss + button.js
dialog.html + dialog.scss + dialog.js
Skipped (already exist):
(none)
Proceed? [Enter to continue, Ctrl+C to cancel]
Wait for confirmation before proceeding.
Generate leaf dependencies first, then composite components.
For each file:
The HTML output is a fragment, not a full document. It begins with the component's root element and contains slot placeholders as plain text where the React version interpolates {children} — for example, <!-- slot: children -->. Users replace those comments with their content.
.html)<html> / <head> / <body> wrapper.btn, card, alert, etc.) so the SCSS works unchanged.data-* attributes (data-btn, data-style, data-color, data-card, data-severity).aria-disabled, aria-labelledby, role only when needed).<!-- slot: children -->, <!-- slot: title -->.<!-- variant: primary -->..scss)Byte-identical to the TSX generator's output. The framework-agnostic CSS is the foundation that lets both generators coexist.
Rules — same as the TSX skill:
--{component}-{element?}-{variant?}-{property}.[aria-disabled="true"] styles are present on every interactive component..js) — components with runtime behaviorEmitted for: Button (aria-disabled wrap), Card (interactive variant only — keyboard activation + card:activate event), Alert (dismiss + auto-hide + pause-on-hover), Dialog (showModal + backdrop close), Popover, Checkbox, Input, IconButton. Stateless markup components (Img, Link, Icon, List, Table, Field, Nav, plain Card) emit no .js file.
wireDisabled from ./_stateful.js when the component participates in the disabled-state pattern.init function that the user calls (e.g. import { init } from './components/html/dialog.js'; init();).init() may be called multiple times without double-binding listeners.See references/stateful-js-patterns.md for the canonical patterns (disabled state, dialog open/close, popover wiring, input validation messages).
The same patterns as the TSX generator — see ${CLAUDE_PLUGIN_ROOT}/skills/components/references/accessibility.md. The HTML output preserves them:
aria-disabled="true" (paired with the is-disabled class) — never the native disabled attribute, so disabled controls stay focusable (WCAG 2.1.1).:focus-visible outline preserved by the SCSS.<button> not <div role="button">, <dialog> not <div role="dialog">).aria-label.After all files are generated, show:
Generated HTML components in components/html/:
Created:
button.html
button.scss
button.js
Skipped (already existed):
_stateful.js
How to wire it up:
1. Compile the SCSS to CSS first — browsers cannot load .scss directly:
npx sass components/html/button.scss components/html/button.css
Then add to your page <head>:
<link rel="stylesheet" href="components/html/button.css">
(If you already have a Sass build pipeline, @import the .scss from
your existing entrypoint instead.)
2. <script type="module" src="components/html/button.js"></script>
3. Paste the markup from button.html into your page or template.
After Step E, run python3 ${CLAUDE_PLUGIN_ROOT}/scripts/verify_html_integration.py <project_root> to check whether the user's pages reference the generated artifacts.
.scss / .js artifact is referenced by at least one page (.html, .css, .scss, .tsx, .vue, .svelte, etc.). No further action.reasons array. Print each reason as a numbered fix-up list with the suggested <link> / <script> snippet. Do not auto-edit the user's pages — the user must add the references themselves.*.html snippets are listed but not checked — they're fragments meant to be copy-pasted, so absence of an automatic reference is expected.
| Document | Purpose |
|----------|---------|
| references/stateful-js-patterns.md | Vanilla-JS recipes (disabled state, dialog showModal, popover wiring, input validation) |
| ${CLAUDE_PLUGIN_ROOT}/skills/components/references/components/catalog.md | Component catalog + HTML Status column |
| ${CLAUDE_PLUGIN_ROOT}/skills/components/references/accessibility.md | WCAG patterns (shared with the TSX generator) |
| ${CLAUDE_PLUGIN_ROOT}/skills/components/references/css-variables.md | CSS variable conventions (shared with the TSX generator) |
<html>/<body> wrappers; the user owns the page shell._stateful.js is the disabled-state helper — copied once per project, imported by stateful components.When a component reference doc gains HTML output:
## HTML Template section after ## TSX Template. Use a fenced html block. Mirror the TSX output exactly — same root element, same classes, same data-* attributes, same ARIA. Use HTML comments for slot placeholders (<!-- slot: children -->).## Vanilla JS section with a fenced js block. Import wireDisabled from ./_stateful.js when relevant. Export an idempotent init() function.references/components/catalog.md — set HTML Status to Verified for that component, with any intentional divergences noted (e.g. "controlled state not represented; user must wire their own").tests/run.sh to confirm the SCSS contract still passes.development
Internal orchestrator for /kit-create, /kit-list, /kit-sync, /kit-update and Form/HTML/Style-Tune modes. Per-component generation lives in component-<name> skills; do not auto-trigger for component requests.
data-ai
Use when the user asks to generate, create, or scaffold a Table — accessible data table with caption, scope headers, responsive scroll wrapper, and sortable column support.
tools
Use when the user asks to generate, create, or scaffold a Popover — accessible tooltip/popover using the Popover API with focus trap, aria-expanded, and light-dismiss.
tools
Use when the user asks to generate, create, or scaffold a Nav — accessible navigation landmark with aria-label, current-page link marking, and horizontal/vertical layout.