shadcn-html / introduction

A UI component system
that scales with AI.

Themeable components built on semantic HTML, modern CSS, and vanilla JavaScript. No framework. No build step. No abstraction layers to get wrong. The simplest possible foundation for AI-driven prototyping.

Themeable

Full shadcn semantic token model. Swap a tweakcn theme and every component updates instantly.

Component Skills

Every component includes a structured skill — markup, variants, ARIA, and wiring conventions — grounded in web standards.

Accessible

Built on native HTML elements and WAI-ARIA patterns. Keyboard navigation, focus management, and screen reader support by default.

Framework Free

Runs in any browser, zero dependencies, no build pipeline required.

Built on five layers.

Tokens. Component skill. CSS. HTML. JavaScript (if needed). That's the whole system.

1 — semantic tokens
/* default-semantic-tokens.css — the design system */
:root {
  --background:         oklch(1 0 0);
  --foreground:         oklch(0.145 0.005 285);
  --primary:            oklch(0.205 0.005 285);
  --primary-foreground: oklch(0.985 0.002 247);
  --border:             oklch(0.88 0.004 247);
  --radius:             0.625rem;
  /* ...all color pairs, shadows, spacing */
}
.dark { /* same keys, dark values */ }
2 — component-skill.md file
# Pattern: Button

## Native basis
`<button>` element. Also works on `<a>` for link-style buttons.

## Native Web APIs
- `<button>` — native clickable element with built-in keyboard and form support
- `:focus-visible` — keyboard-only focus ring
- `commandfor` / `command` — declarative button→dialog/popover triggers without JS
  # ...prefers-reduced-motion, prefers-contrast, forced-colors

## Structure
<button class="btn" data-variant="default">Click me</button>

## Variants
| data-variant  | Surface              | Text                     | Hover           |
|---------------|----------------------|--------------------------|-----------------|
| default       | --primary            | --primary-foreground     | opacity: 0.88   |
| secondary     | --secondary          | --secondary-foreground   | opacity: 0.8    |
| outline       | --background + border | --foreground             | --accent bg     |
| ghost         | transparent          | --foreground             | --accent bg     |
| destructive   | --destructive        | --destructive-foreground | opacity: 0.88   |
| link          | transparent          | --primary                | underline       |
  # ...sizes, accessibility, notes
3 — component CSS
/* button.css — styled with tokens */
@layer components {
  .btn {
    display: inline-flex;
    align-items: center;
    border-radius: var(--radius-md);
    font-weight: 500;
  }
  .btn[data-variant="default"] {
    background: var(--primary);
    color: var(--primary-foreground);
  }
  /* ...secondary, destructive, outline, ghost, link */
}
4 — semantic HTML
<button class="btn" data-variant="default"
        data-dialog-trigger="confirm">
  Open Dialog
</button>

<dialog id="confirm" class="dialog">
  <div class="dialog-content">...</div>
</dialog>
5 — vanilla JavaScript (if needed)
// dialog.js — wire interactivity
function init() {
  document.querySelectorAll('[data-dialog-trigger]:not([data-init])').forEach((trigger) => {
    trigger.dataset.init = '';
    const dialog = document.getElementById(trigger.dataset.dialogTrigger);
    if (!dialog) return;
    trigger.addEventListener('click', () => dialog.showModal());
  });
  /* ...close-on-backdrop, [data-dialog-close] buttons, focus restore */
}

init();
new MutationObserver(init).observe(document, { childList: true, subtree: true });