445 lines
24 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="PettyUI showcase — 45 headless Web Components, zero dependencies, built on browser standards." />
<title>PettyUI — Component Showcase</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@400;500;600;700&family=SUSE+Mono:wght@400;500;600;700&display=swap" rel="stylesheet" />
<script type="module">
import "pettyui/button"; import "pettyui/link"; import "pettyui/separator";
import "pettyui/badge"; import "pettyui/skeleton"; import "pettyui/avatar";
import "pettyui/card"; import "pettyui/text-field"; import "pettyui/checkbox";
import "pettyui/switch"; import "pettyui/radio-group"; import "pettyui/toggle";
import "pettyui/toggle-group"; import "pettyui/number-field"; import "pettyui/slider";
import "pettyui/tags-input"; import "pettyui/select"; import "pettyui/tabs";
import "pettyui/accordion"; import "pettyui/collapsible"; import "pettyui/breadcrumbs";
import "pettyui/pagination"; import "pettyui/dialog"; import "pettyui/alert-dialog";
import "pettyui/popover"; import "pettyui/tooltip"; import "pettyui/dropdown-menu";
import "pettyui/alert"; import "pettyui/progress"; import "pettyui/meter";
import "pettyui/toast"; import { toast } from "pettyui/toast";
import "pettyui/typewriter"; import "pettyui/counter";
import "pettyui/stagger"; import "pettyui/reveal";
import "pettyui/loading-indicator"; import "pettyui/animations";
document.querySelector("#demo-toast-btn")?.addEventListener("click", () => {
toast.success("Component loaded", { description: "PettyUI runs with zero deps" });
});
document.querySelector("#demo-toast-err")?.addEventListener("click", () => {
toast.error("Something broke", { description: "Just kidding" });
});
</script>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<a href="#main" class="skip-link">Skip to content</a>
<main class="page" id="main">
<section class="hero">
<h1><petty-typewriter speed="60">PettyUI</petty-typewriter></h1>
<p class="hero-sub"><petty-typewriter speed="25" delay="600">45 headless Web Components. Zero dependencies. Browser-native APIs. The smallest UI library that does everything.</petty-typewriter></p>
<div class="hero-stats">
<span class="hero-stat"><petty-counter to="45" duration="1500" delay="1200"></petty-counter> components</span>
<span class="hero-stat-dot" aria-hidden="true"></span>
<span class="hero-stat"><petty-counter to="0" duration="500" delay="1400"></petty-counter> dependencies</span>
<span class="hero-stat-dot" aria-hidden="true"></span>
<span class="hero-stat">~<petty-counter to="500" duration="1200" delay="1600" suffix="B"></petty-counter> runtime</span>
</div>
<div class="hero-mcp">
<span class="hero-mcp-label">MCP</span>
<code class="hero-mcp-url">npx pettyui --mcp</code>
<span class="hero-mcp-sep">or</span>
<a href="https://petty.staythree.com/mcp" class="hero-mcp-link">petty.staythree.com/mcp</a>
</div>
</section>
<section class="manifesto">
<div class="manifesto-inner">
<p class="manifesto-kicker">// src/philosophy.txt</p>
<h2 class="manifesto-heading">Your browser already has a UI framework.<br>You just forgot.</h2>
<div class="manifesto-body">
<p>Somewhere around 2015 we collectively decided that browsers were too stupid to build interfaces. So we invented virtual DOMs, reconcilers, hydration strategies, and server components to work around a platform that wasn't ready. Fair enough. It wasn't.</p>
<p>It is now.</p>
<p>Native <code>&lt;dialog&gt;</code> with focus trapping. Popover API with light-dismiss. Anchor positioning. CSS <code>:has()</code>. Custom Elements that just work in every framework, or in none at all. The browser shipped the abstractions we spent a decade rebuilding in userland &mdash; and most of us didn't notice because we were busy migrating to the next meta-framework.</p>
<p>PettyUI is what happens when you stop fighting the platform and start reading the spec. 45 components, zero dependencies, ~500 bytes of runtime. Not because minimalism is trendy, but because there's genuinely nothing left to add. The browser does the hard parts. We just wire up the keyboard shortcuts and ARIA.</p>
<p>Now that AI writes most of the glue code anyway, do you really need 140KB of framework opinions between your intent and the DOM? For a dashboard? For a form? Really?</p>
<p>We're not saying frameworks are dead. We're saying the default answer to "how do I build a UI" shouldn't be "install 900 packages and pray." For most things most people ship, Custom Elements + the platform APIs are not just <em>enough</em> &mdash; they're <em>better</em>. Faster, smaller, no build step required, and they'll still work when whatever you're using today is in maintenance mode.</p>
<p>This library exists because building things that are simply better is more fun than arguing about it online.</p>
</div>
</div>
</section>
<section class="value-props">
<h2 class="sr-only">Why PettyUI</h2>
<dl class="value-list">
<div class="value-item">
<dt>Browser-Native</dt>
<dd>Popover API, native Dialog, Navigation API, Invoker Commands. No polyfills.</dd>
</div>
<div class="value-item">
<dt>Zero Runtime</dt>
<dd>500-byte signals. No virtual DOM, no framework. Just Custom Elements.</dd>
</div>
<div class="value-item">
<dt>AI-Native</dt>
<dd>Zod schemas for every prop. MCP tools for agent integration.</dd>
</div>
</dl>
</section>
<div class="section-separator"></div>
<section class="section">
<petty-reveal animation="fade-up" once><h2 class="section-title">Inputs &amp; Forms</h2></petty-reveal>
<petty-reveal animation="fade-up" once delay="100"><p class="section-desc">Form primitives with built-in ARIA, keyboard navigation, and event handling.</p></petty-reveal>
<div class="grid">
<article class="demo-card">
<h3>Button</h3>
<div class="demo-row">
<petty-button><button type="button" class="primary">Filled</button></petty-button>
<petty-button><button type="button" class="tonal">Tonal</button></petty-button>
<petty-button><button type="button">Outlined</button></petty-button>
<petty-button disabled><button type="button">Disabled</button></petty-button>
</div>
<p class="tag-name">&lt;petty-button&gt;</p>
</article>
<article class="demo-card">
<h3>Text Field</h3>
<petty-text-field name="email">
<label data-part="label">Email address</label>
<input data-part="control" type="email" placeholder="you@example.com" aria-label="Email address" />
<span data-part="description">We will never share your email.</span>
<span data-part="error"></span>
</petty-text-field>
<p class="tag-name">&lt;petty-text-field&gt;</p>
</article>
<article class="demo-card">
<h3>Checkbox &amp; Switch</h3>
<div class="demo-area">
<petty-checkbox>
<input data-part="control" type="checkbox" aria-label="Accept terms" />
<label data-part="label">Accept terms</label>
</petty-checkbox>
<petty-checkbox checked>
<input data-part="control" type="checkbox" aria-label="Subscribe to updates" />
<label data-part="label">Subscribe to updates</label>
</petty-checkbox>
<petty-separator></petty-separator>
<petty-switch>
<button type="button" data-part="control" role="switch" aria-checked="false" aria-label="Dark mode"><span data-part="thumb"></span></button>
<span data-part="label">Dark mode</span>
</petty-switch>
</div>
<p class="tag-name">&lt;petty-checkbox&gt; &lt;petty-switch&gt;</p>
</article>
<article class="demo-card">
<h3>Radio Group</h3>
<petty-radio-group default-value="md" orientation="vertical">
<petty-radio-item value="sm">Small</petty-radio-item>
<petty-radio-item value="md">Medium</petty-radio-item>
<petty-radio-item value="lg">Large</petty-radio-item>
</petty-radio-group>
<p class="tag-name">&lt;petty-radio-group&gt;</p>
</article>
<article class="demo-card">
<h3>Toggle &amp; Toggle Group</h3>
<div class="demo-area">
<div class="demo-row">
<petty-toggle><button type="button" data-part="control">Mute</button></petty-toggle>
<petty-toggle pressed><button type="button" data-part="control">Pin</button></petty-toggle>
</div>
<petty-toggle-group type="single" default-value="left">
<petty-toggle-group-item value="left">Left</petty-toggle-group-item>
<petty-toggle-group-item value="center">Center</petty-toggle-group-item>
<petty-toggle-group-item value="right">Right</petty-toggle-group-item>
</petty-toggle-group>
</div>
<p class="tag-name">&lt;petty-toggle&gt; &lt;petty-toggle-group&gt;</p>
</article>
<article class="demo-card">
<h3>Number Field &amp; Slider</h3>
<petty-number-field min="0" max="99" step="1" value="3">
<label data-part="label">Qty</label>
<button type="button" data-part="decrement">&minus;</button>
<input data-part="control" type="number" aria-label="Quantity" />
<button type="button" data-part="increment">+</button>
</petty-number-field>
<petty-slider min="0" max="100" value="65">
<label data-part="label">Volume</label>
<input data-part="control" type="range" aria-label="Volume" />
<output data-part="output">65</output>
</petty-slider>
<p class="tag-name">&lt;petty-number-field&gt; &lt;petty-slider&gt;</p>
</article>
<article class="demo-card">
<h3>Select</h3>
<petty-select default-value="ts" placeholder="Pick a language">
<button type="button" data-part="trigger" popovertarget="lang-sel">Pick a language</button>
<div id="lang-sel" popover data-part="listbox" role="listbox">
<petty-select-option value="ts">TypeScript</petty-select-option>
<petty-select-option value="rs">Rust</petty-select-option>
<petty-select-option value="go">Go</petty-select-option>
<petty-select-option value="py">Python</petty-select-option>
</div>
</petty-select>
<p class="tag-name">&lt;petty-select&gt;</p>
</article>
<article class="demo-card">
<h3>Tags Input</h3>
<petty-tags-input value="pettyui,web-components,headless">
<span data-part="tags"></span>
<input data-part="input" placeholder="Add tag..." aria-label="Add tag" />
<input data-part="hidden" type="hidden" aria-label="Selected tags" />
</petty-tags-input>
<p class="tag-name">&lt;petty-tags-input&gt;</p>
</article>
</div>
</section>
<section class="section section-glow">
<petty-reveal animation="fade-up" once><h2 class="section-title">Navigation</h2></petty-reveal>
<petty-reveal animation="fade-up" once delay="100"><p class="section-desc">Tab bars, accordions, breadcrumbs, and pagination &mdash; all keyboard-accessible.</p></petty-reveal>
<div class="grid">
<article class="demo-card wide">
<h3>Tabs</h3>
<petty-tabs default-value="overview">
<div role="tablist">
<petty-tab value="overview">Overview</petty-tab>
<petty-tab value="features">Features</petty-tab>
<petty-tab value="api">API</petty-tab>
</div>
<petty-tab-panel value="overview">PettyUI is a headless component library built on vanilla Web Components. No framework required.</petty-tab-panel>
<petty-tab-panel value="features">Zero dependencies, Popover API, native dialog, Invoker Commands, Navigation API, View Transitions.</petty-tab-panel>
<petty-tab-panel value="api">Every component is a Custom Element. Import and use anywhere: React, Vue, Svelte, or plain HTML.</petty-tab-panel>
</petty-tabs>
<p class="tag-name">&lt;petty-tabs&gt;</p>
</article>
<article class="demo-card">
<h3>Accordion</h3>
<petty-accordion type="single">
<petty-accordion-item value="install">
<details><summary>Installation</summary><p data-part="content">npm install pettyui &mdash; zero peer dependencies.</p></details>
</petty-accordion-item>
<petty-accordion-item value="usage">
<details><summary>Usage</summary><p data-part="content">Import the component, write HTML. That is it.</p></details>
</petty-accordion-item>
<petty-accordion-item value="styling">
<details><summary>Styling</summary><p data-part="content">No Shadow DOM. Style with plain CSS, Tailwind, or anything.</p></details>
</petty-accordion-item>
</petty-accordion>
<p class="tag-name">&lt;petty-accordion&gt;</p>
</article>
<article class="demo-card">
<h3>Breadcrumbs &amp; Pagination</h3>
<petty-breadcrumbs>
<ol>
<petty-breadcrumb-item><a href="/home">Home</a></petty-breadcrumb-item>
<petty-breadcrumb-item><a href="/components">Components</a></petty-breadcrumb-item>
<petty-breadcrumb-item current>Button</petty-breadcrumb-item>
</ol>
</petty-breadcrumbs>
<petty-separator></petty-separator>
<petty-pagination total="50" page-size="10" current-page="2">
<nav aria-label="Pagination">
<petty-pagination-item type="prev">&larr;</petty-pagination-item>
<petty-pagination-item value="1">1</petty-pagination-item>
<petty-pagination-item value="2">2</petty-pagination-item>
<petty-pagination-item value="3">3</petty-pagination-item>
<petty-pagination-item value="4">4</petty-pagination-item>
<petty-pagination-item value="5">5</petty-pagination-item>
<petty-pagination-item type="next">&rarr;</petty-pagination-item>
</nav>
</petty-pagination>
<p class="tag-name">&lt;petty-breadcrumbs&gt; &lt;petty-pagination&gt;</p>
</article>
</div>
</section>
<section class="section">
<petty-reveal animation="fade-up" once><h2 class="section-title">Overlays</h2></petty-reveal>
<petty-reveal animation="fade-up" once delay="100"><p class="section-desc">Dialogs, popovers, tooltips, and drawers &mdash; powered by native browser APIs.</p></petty-reveal>
<div class="grid">
<article class="demo-card">
<h3>Dialog &amp; Alert Dialog</h3>
<div class="demo-row">
<petty-button><button type="button" onclick="document.getElementById('demo-dlg').showModal()">Open Dialog</button></petty-button>
<petty-button><button type="button" onclick="document.getElementById('demo-alert-dlg').showModal()">Confirm Delete</button></petty-button>
</div>
<p class="tag-name">&lt;petty-dialog&gt; &lt;petty-alert-dialog&gt;</p>
</article>
<article class="demo-card">
<h3>Popover &amp; Tooltip</h3>
<div class="demo-row">
<petty-popover>
<button type="button" class="btn-outline" popovertarget="demo-pop">Show Popover</button>
<div id="demo-pop" popover data-part="content">This uses the native Popover API. Click outside to dismiss.</div>
</petty-popover>
<petty-tooltip>
<span data-part="trigger" tabindex="0" class="hint-text">Hover me</span>
<div popover="manual" data-part="content" role="tooltip">Native tooltip via Popover API</div>
</petty-tooltip>
</div>
<p class="tag-name">&lt;petty-popover&gt; &lt;petty-tooltip&gt;</p>
</article>
<article class="demo-card">
<h3>Dropdown Menu</h3>
<petty-dropdown-menu>
<button type="button" data-part="trigger" class="btn-outline" popovertarget="demo-menu">Actions &#x25BE;</button>
<div id="demo-menu" popover data-part="content" role="menu">
<petty-menu-item>Edit</petty-menu-item>
<petty-menu-item>Duplicate</petty-menu-item>
<petty-menu-item>Archive</petty-menu-item>
<petty-menu-item disabled>Delete</petty-menu-item>
</div>
</petty-dropdown-menu>
<p class="tag-name">&lt;petty-dropdown-menu&gt;</p>
</article>
</div>
</section>
<section class="section section-glow">
<petty-reveal animation="fade-up" once><h2 class="section-title">Feedback &amp; Display</h2></petty-reveal>
<petty-reveal animation="fade-up" once delay="100"><p class="section-desc">Status indicators, progress bars, alerts, badges, and notifications.</p></petty-reveal>
<div class="grid">
<article class="demo-card">
<h3>Alert</h3>
<div class="demo-area">
<petty-alert variant="success"><span class="alert-icon" aria-hidden="true">&#10003;</span><div data-part="content"><div data-part="title">Deployed successfully</div><div data-part="description">Your changes are live and visible to all users.</div></div></petty-alert>
<petty-alert variant="error"><span class="alert-icon" aria-hidden="true">&#10007;</span><div data-part="content"><div data-part="title">Build failed</div><div data-part="description">Check the CI logs for error details.</div></div></petty-alert>
<petty-alert variant="warning"><span class="alert-icon" aria-hidden="true">&#9888;</span><div data-part="content"><div data-part="title">Rate limit approaching</div><div data-part="description">You have used 90% of your API quota this month.</div></div></petty-alert>
</div>
<p class="tag-name">&lt;petty-alert&gt;</p>
</article>
<article class="demo-card">
<h3>Progress &amp; Meter</h3>
<div class="demo-area">
<div class="progress-field">
<div class="progress-header"><span>Upload progress</span><span>72%</span></div>
<petty-progress value="72" max="100"><div data-part="track"><div data-part="fill"></div></div></petty-progress>
</div>
<div class="progress-field">
<div class="progress-header"><span>CPU usage</span><span class="meter-low">35%</span></div>
<petty-meter value="35" min="0" max="100" low="25" high="75" optimum="50"><div data-part="track"><div data-part="fill"></div></div></petty-meter>
</div>
<div class="progress-field">
<div class="progress-header"><span>Memory</span><span class="meter-high">85%</span></div>
<petty-meter value="85" min="0" max="100" low="25" high="75" optimum="50"><div data-part="track"><div data-part="fill"></div></div></petty-meter>
</div>
</div>
<div class="progress-field">
<div class="progress-header"><span>Loading indicator</span></div>
<div class="demo-row-spacious loading-demo">
<petty-loading-indicator size="20" aria-label="Loading tiny"></petty-loading-indicator>
<petty-loading-indicator size="28" aria-label="Loading small"></petty-loading-indicator>
<petty-loading-indicator size="36" aria-label="Loading medium"></petty-loading-indicator>
<petty-loading-indicator aria-label="Loading default"></petty-loading-indicator>
</div>
</div>
<p class="tag-name">&lt;petty-progress&gt; &lt;petty-meter&gt; &lt;petty-loading-indicator&gt;</p>
</article>
<article class="demo-card">
<h3>Badge &amp; Avatar</h3>
<div class="demo-area">
<div class="demo-row-loose">
<petty-badge>Default</petty-badge>
<petty-badge variant="success">Active</petty-badge>
<petty-badge variant="error">Offline</petty-badge>
<petty-badge variant="warning">Pending</petty-badge>
</div>
<div class="demo-row-loose">
<petty-avatar><span data-part="fallback">MB</span></petty-avatar>
<petty-avatar><span data-part="fallback">JD</span></petty-avatar>
<petty-avatar><span data-part="fallback">AK</span></petty-avatar>
</div>
</div>
<p class="tag-name">&lt;petty-badge&gt; &lt;petty-avatar&gt;</p>
</article>
<article class="demo-card">
<h3>Toast</h3>
<div class="demo-row">
<petty-button><button type="button" id="demo-toast-btn" class="primary">Success Toast</button></petty-button>
<petty-button><button type="button" id="demo-toast-err">Error Toast</button></petty-button>
</div>
<p class="tag-name">&lt;petty-toast-region&gt;</p>
</article>
<article class="demo-card">
<h3>Skeleton &amp; Card</h3>
<petty-card>
<petty-card-header><h4>Loading content</h4></petty-card-header>
<petty-card-content>
<petty-skeleton class="skel-80"></petty-skeleton>
<petty-skeleton class="skel-60"></petty-skeleton>
<petty-skeleton class="skel-70"></petty-skeleton>
</petty-card-content>
<petty-card-footer>
<petty-skeleton class="skel-btn"></petty-skeleton>
</petty-card-footer>
</petty-card>
<p class="tag-name">&lt;petty-card&gt; &lt;petty-skeleton&gt;</p>
</article>
<article class="demo-card">
<h3>Link &amp; Separator</h3>
<petty-link><a href="/docs">Internal documentation link</a></petty-link>
<petty-link external><a href="https://developer.mozilla.org" target="_blank" rel="noopener">MDN Web Docs &#x2197;</a></petty-link>
<petty-separator></petty-separator>
<span class="muted-sm">Separator shown above</span>
<p class="tag-name">&lt;petty-link&gt; &lt;petty-separator&gt;</p>
</article>
</div>
</section>
<section class="footer-section">
<petty-separator></petty-separator>
<p class="footer-text">PettyUI v2 &mdash; 45 Web Components, 0 dependencies, built on browser standards.</p>
</section>
<footer class="st3-sig"><span>//ST3</span></footer>
</main>
<petty-dialog>
<dialog id="demo-dlg">
<h2>Welcome to PettyUI</h2>
<p>This dialog uses the native dialog element. Focus trap, backdrop, and Escape key work out of the box.</p>
<petty-button><button type="button" onclick="document.getElementById('demo-dlg').close()">Got it</button></petty-button>
</dialog>
</petty-dialog>
<petty-alert-dialog>
<dialog id="demo-alert-dlg" role="alertdialog">
<h2>Delete project?</h2>
<p>This action cannot be undone. All data will be permanently removed.</p>
<div class="demo-row">
<petty-button><button type="button" onclick="document.getElementById('demo-alert-dlg').close()">Cancel</button></petty-button>
<petty-button><button type="button" class="primary" onclick="document.getElementById('demo-alert-dlg').close()">Delete</button></petty-button>
</div>
</dialog>
</petty-alert-dialog>
<petty-toast-region></petty-toast-region>
</body>
</html>