Menu
AkurAI-Build
publicLatest change 0cf28150faedd8c150474a4f16be4c9922c04f56 - feat: add Ponytail UI design system by Olafur Bui
{% extends "layouts/base.html" %}
{% block title %}Ponytail UI component catalog · Bunfork{% endblock %}
{% block description %}Twenty accessible component genres built from Bunfork's CSS-first Ponytail UI tokens and native HTML controls.{% endblock %}
{% block body_class %}pt-catalog{% endblock %}
{% block body %}
<main id="main-content" tabindex="-1">
<header class="pt-container pt-catalog-hero">
<p class="pt-meta">Ponytail UI component catalog</p>
<h1 class="pt-display">Twenty patterns. One small vocabulary.</h1>
<p class="pt-lede">Semantic HTML, shared tokens, and native browser behavior cover common interface patterns without a client framework.</p>
<nav class="pt-cluster" aria-label="Catalog resources">
<a class="pt-button" href="/docs/design-system">Read the design guide</a>
<a class="pt-button" data-variant="secondary" href="#component-genres">Browse components</a>
</nav>
</header>
<section class="pt-theme-band" aria-labelledby="catalog-themes">
<h2 id="catalog-themes" class="pt-visually-hidden">Theme laboratory</h2>
<form class="pt-container pt-theme-controls" aria-label="Theme preview controls">
<fieldset class="pt-theme-options">
<legend>Mode</legend>
<label class="pt-check"><input id="catalog-mode-auto" type="radio" name="catalog-mode" checked> Auto</label>
<label class="pt-check"><input id="catalog-mode-light" type="radio" name="catalog-mode"> Light</label>
<label class="pt-check"><input id="catalog-mode-dark" type="radio" name="catalog-mode"> Dark</label>
</fieldset>
<fieldset class="pt-theme-options">
<legend>Palette</legend>
<label class="pt-check"><input id="catalog-palette-fork" type="radio" name="catalog-palette" checked> Fork</label>
<label class="pt-check"><input id="catalog-palette-tide" type="radio" name="catalog-palette"> Tide</label>
<label class="pt-check"><input id="catalog-palette-ember" type="radio" name="catalog-palette"> Ember</label>
<label class="pt-check"><input id="catalog-palette-berry" type="radio" name="catalog-palette"> Berry</label>
</fieldset>
</form>
<div class="pt-container pt-theme-grid">
<div class="pt-theme-swatch" data-mode="light" data-palette="fork"><strong>Fork</strong><small>Light</small><i aria-hidden="true"></i></div>
<div class="pt-theme-swatch" data-mode="dark" data-palette="fork"><strong>Fork</strong><small>Dark</small><i aria-hidden="true"></i></div>
<div class="pt-theme-swatch" data-mode="light" data-palette="tide"><strong>Tide</strong><small>Light</small><i aria-hidden="true"></i></div>
<div class="pt-theme-swatch" data-mode="dark" data-palette="ember"><strong>Ember</strong><small>Dark</small><i aria-hidden="true"></i></div>
</div>
</section>
<div id="component-genres" class="pt-container pt-catalog-list">
<section class="pt-specimen" data-component-genre="actions" aria-labelledby="genre-actions">
<div class="pt-specimen-copy">
<h2 id="genre-actions">Actions and toolbars</h2>
<p>One clear primary action, then quieter alternatives. Native disabled state removes false affordance.</p>
<p class="pt-source">Ideas: <a href="https://github.com/radix-ui/themes">Radix Themes</a> and <a href="https://github.com/shadcn-ui/ui">shadcn/ui</a>.</p>
</div>
<div class="pt-demo">
<div class="pt-toolbar" aria-label="Document actions">
<button class="pt-button" type="button">Save draft</button>
<button class="pt-button" data-variant="secondary" type="button">Preview</button>
<button class="pt-button" data-variant="quiet" type="button">Duplicate</button>
<button class="pt-button" type="button" disabled>Publishing</button>
</div>
</div>
</section>
<section class="pt-specimen" data-component-genre="text-fields" aria-labelledby="genre-fields">
<div class="pt-specimen-copy">
<h2 id="genre-fields">Text fields</h2>
<p>Visible labels, persistent help, stable IDs, and errors beside the field survive server round trips.</p>
<p class="pt-source">Ideas: <a href="https://github.com/alphagov/govuk-frontend">GOV.UK Frontend</a> and <a href="https://github.com/uswds/uswds">USWDS</a>.</p>
</div>
<form class="pt-demo pt-stack" action="/examples/components" method="get">
<div class="pt-field">
<label for="project-name">Project name</label>
<p id="project-name-hint" class="pt-hint">Use 3 to 40 characters.</p>
<input id="project-name" class="pt-input" name="project" value="Bunfork native" aria-describedby="project-name-hint">
</div>
<div class="pt-field">
<label for="public-origin">Public origin</label>
<input id="public-origin" class="pt-input" name="origin" value="not-a-valid-origin" aria-invalid="true" aria-describedby="public-origin-error">
<p id="public-origin-error" class="pt-error">Enter a complete HTTPS origin.</p>
</div>
<div class="pt-field">
<label for="release-note">Release note</label>
<textarea id="release-note" class="pt-textarea" name="note" rows="3">Verified native theme controls.</textarea>
</div>
<div class="pt-field">
<label for="manifest-file">Manifest file</label>
<input id="manifest-file" class="pt-input" type="file" name="manifest" accept="application/json,.json">
</div>
<div class="pt-field">
<label for="accent-sample">Accent sample</label>
<input id="accent-sample" class="pt-input pt-color-input" type="color" name="accent" value="#176c3a">
</div>
</form>
</section>
<section class="pt-specimen" data-component-genre="choice-controls" aria-labelledby="genre-choices">
<div class="pt-specimen-copy">
<h2 id="genre-choices">Choice controls</h2>
<p>Token-skinned radio, checkbox, switch, select, and range controls keep native semantics without browser-default visuals.</p>
<p class="pt-source">Ideas: <a href="https://github.com/material-components/material-web">Material Web</a> and <a href="https://github.com/twbs/bootstrap">Bootstrap</a>.</p>
</div>
<form class="pt-demo pt-stack" action="/examples/components" method="get">
<fieldset class="pt-check-list">
<legend class="pt-legend">Serving mode</legend>
<label class="pt-check"><input type="radio" name="mode" value="native" checked> Native pages</label>
<label class="pt-check"><input type="radio" name="mode" value="static"> Static artifact</label>
</fieldset>
<label class="pt-check"><input type="checkbox" name="verify" checked> Verify before bind</label>
<label class="pt-check"><input type="checkbox" role="switch" name="preload" checked> Preload admitted files</label>
<div class="pt-field">
<label for="cache-policy">Cache policy</label>
<select id="cache-policy" class="pt-select" name="cache">
<option>Revalidate</option>
<option>Immutable namespace</option>
</select>
</div>
<div class="pt-field">
<label for="request-budget">Request budget</label>
<input id="request-budget" class="pt-range" type="range" min="0" max="100" value="72">
</div>
</form>
</section>
<section class="pt-specimen" data-component-genre="validation" aria-labelledby="genre-validation">
<div class="pt-specimen-copy">
<h2 id="genre-validation">Validation summary</h2>
<p>A summary gives orientation while each field keeps its specific cause and recovery path.</p>
<p class="pt-source">Ideas: <a href="https://design-system.service.gov.uk/components/error-summary/">GOV.UK error summary</a>.</p>
</div>
<div class="pt-demo pt-alert" data-tone="danger" role="group" aria-labelledby="validation-title">
<strong id="validation-title">Two fields need attention</strong>
<ul>
<li><a href="#project-name">Check the project name</a></li>
<li><a href="#public-origin">Enter an HTTPS origin</a></li>
</ul>
</div>
</section>
<section class="pt-specimen" data-component-genre="search" aria-labelledby="genre-search">
<div class="pt-specimen-copy">
<h2 id="genre-search">Search and command input</h2>
<p>A standard search form works by URL. Suggestions wait for a fully themed, keyboard-tested combobox rather than exposing a browser popup.</p>
<p class="pt-source">Ideas: <a href="https://github.com/primer/primer">Primer</a> and <a href="https://github.com/shadcn-ui/ui">shadcn/ui</a>.</p>
</div>
<form class="pt-demo pt-search" role="search" action="/examples/components" method="get">
<label class="pt-visually-hidden" for="catalog-search">Search components</label>
<input id="catalog-search" class="pt-input" type="search" name="q" placeholder="Search components">
<button class="pt-button" type="submit">Search</button>
</form>
</section>
<section class="pt-specimen" data-component-genre="navigation" aria-labelledby="genre-navigation">
<div class="pt-specimen-copy">
<h2 id="genre-navigation">Primary navigation</h2>
<p>Labels stay visible, the current destination is announced, and every item has a robust target.</p>
<p class="pt-source">Ideas: <a href="https://github.com/primer/primer">Primer</a> and <a href="https://github.com/microsoft/fluentui">Fluent UI</a>.</p>
</div>
<div class="pt-demo">
<nav class="pt-segmented" aria-label="Example application">
<a href="#genre-navigation" aria-current="page">Overview</a>
<a href="#genre-tables">Releases</a>
<a href="#genre-activity">Activity</a>
</nav>
</div>
</section>
<section class="pt-specimen" data-component-genre="breadcrumbs-pagination" aria-labelledby="genre-wayfinding">
<div class="pt-specimen-copy">
<h2 id="genre-wayfinding">Breadcrumbs and pagination</h2>
<p>Location and sequence use ordinary links, ordered structure, and explicit current state.</p>
<p class="pt-source">Ideas: <a href="https://github.com/uswds/uswds">USWDS</a> and <a href="https://github.com/primer/primer">Primer</a>.</p>
</div>
<div class="pt-demo pt-stack">
<nav class="pt-breadcrumbs" aria-label="Example breadcrumb">
<ol><li><a href="/">Home</a></li><li><a href="/docs">Docs</a></li><li aria-current="page">Design system</li></ol>
</nav>
<nav aria-label="Example pagination"><ul class="pt-pagination"><li><a href="#genre-navigation">Previous</a></li><li><span aria-current="page">2</span></li><li><a href="#genre-tabs">3</a></li><li><a href="#genre-tabs">Next</a></li></ul></nav>
</div>
</section>
<section class="pt-specimen" data-component-genre="tabs" aria-labelledby="genre-tabs">
<div class="pt-specimen-copy">
<h2 id="genre-tabs">Linked tabs</h2>
<p>Each tab is a real destination. True in-page tabs wait for tested arrow-key behavior.</p>
<p class="pt-source">Ideas: <a href="https://github.com/radix-ui/primitives">Radix Primitives</a> and <a href="https://github.com/material-components/material-web/tree/main/tabs">Material tabs</a>.</p>
</div>
<div class="pt-demo pt-stack">
<nav aria-label="Release views"><ul class="pt-tabs"><li><a href="#genre-tabs" aria-current="page">Summary</a></li><li><a href="#genre-tables">Files</a></li><li><a href="#genre-progress">Checks</a></li></ul></nav>
<p class="pt-meta">Summary is selected. Links preserve deep-linking and browser history.</p>
</div>
</section>
<section class="pt-specimen" data-component-genre="disclosure" aria-labelledby="genre-disclosure">
<div class="pt-specimen-copy">
<h2 id="genre-disclosure">Disclosure and accordion</h2>
<p>Details and summary provide keyboard, pointer, open state, and no-script fallback in one native element.</p>
<p class="pt-source">Ideas: <a href="https://github.com/alphagov/govuk-frontend">GOV.UK Frontend</a> and the <a href="https://html.spec.whatwg.org/multipage/interactive-elements.html#the-details-element">HTML standard</a>.</p>
</div>
<div class="pt-demo">
<details class="pt-disclosure" name="catalog-faq" open><summary>What is admitted?</summary><p>A completed directory of ordinary browser files with a strict manifest.</p></details>
<details class="pt-disclosure" name="catalog-faq"><summary>What stays outside?</summary><p>Server runtimes, source compilers, secrets, and mutable application data.</p></details>
</div>
</section>
<section class="pt-specimen" data-component-genre="popover" aria-labelledby="genre-popover">
<div class="pt-specimen-copy">
<h2 id="genre-popover">Popover and action menu</h2>
<p>The Popover API owns top-layer placement and dismissal. Essential actions remain ordinary links and buttons.</p>
<p class="pt-source">Ideas: <a href="https://github.com/radix-ui/primitives">Radix Primitives</a> and the <a href="https://html.spec.whatwg.org/multipage/popover.html">HTML Popover standard</a>.</p>
</div>
<div class="pt-demo">
<button class="pt-button pt-popover-trigger" data-variant="secondary" type="button" popovertarget="account-popover">Open account actions</button>
<div id="account-popover" class="pt-popover" popover>
<strong>Signed in as release operator</strong>
<ul class="pt-menu"><li><a href="/docs/deployment">Deployment guide</a></li><li><button type="button">Copy public URL</button></li><li><button type="button">Sign out</button></li></ul>
</div>
</div>
</section>
<section class="pt-specimen" data-component-genre="alerts" aria-labelledby="genre-alerts">
<div class="pt-specimen-copy">
<h2 id="genre-alerts">Alerts and banners</h2>
<p>Color supports a plain-language heading. Static notices do not misuse live-region roles.</p>
<p class="pt-source">Ideas: <a href="https://github.com/carbon-design-system/carbon">Carbon</a> and <a href="https://github.com/uswds/uswds">USWDS</a>.</p>
</div>
<div class="pt-demo pt-stack">
<aside class="pt-alert"><strong>Artifact verified</strong><p>Every byte matches the admitted manifest.</p></aside>
<aside class="pt-alert" data-tone="warning"><strong>Firewall required</strong><p>The default listener accepts traffic on every interface.</p></aside>
<aside class="pt-alert" data-tone="danger"><strong>Restore stopped</strong><p>Quiesce active writers, then retry the verified backup.</p></aside>
</div>
</section>
<section class="pt-specimen" data-component-genre="progress-feedback" aria-labelledby="genre-progress">
<div class="pt-specimen-copy">
<h2 id="genre-progress">Toast, progress, and loading</h2>
<p>Feedback names the work and remains readable without motion. Live announcements belong to the code that inserts them.</p>
<p class="pt-source">Ideas: <a href="https://github.com/carbon-design-system/carbon">Carbon</a> and <a href="https://github.com/microsoft/fluentui">Fluent UI</a>.</p>
</div>
<div class="pt-demo pt-stack">
<div class="pt-toast"><p><strong>Backup complete</strong><br><small>Encrypted file verified.</small></p><button class="pt-button" data-variant="quiet" type="button">Dismiss</button></div>
<label for="release-progress"><strong>Release build</strong></label>
<progress id="release-progress" class="pt-progress" value="72" max="100">72%</progress>
<label for="storage-meter"><strong>Storage headroom</strong></label>
<meter id="storage-meter" class="pt-meter" min="0" low="25" high="80" optimum="60" max="100" value="64">64%</meter>
<div class="pt-stack" aria-label="Loading release details" aria-busy="true"><span class="pt-skeleton" data-width="75"></span><span class="pt-skeleton" data-width="48"></span></div>
</div>
</section>
<section class="pt-specimen" data-component-genre="cards-empty-states" aria-labelledby="genre-cards">
<div class="pt-specimen-copy">
<h2 id="genre-cards">Cards and empty states</h2>
<p>A card represents one bounded object. Empty states explain what is missing and offer one direct next step.</p>
<p class="pt-source">Ideas: <a href="https://github.com/picocss/pico">Pico CSS</a> and <a href="https://github.com/primer/primer">Primer</a>.</p>
</div>
<div class="pt-demo pt-grid">
<article class="pt-card" data-interactive="true"><div class="pt-person-row"><span class="pt-avatar"><img src="/assets/forkbird-avatar.png" width="256" height="256" alt=""></span><div><h3>Native starter</h3><p class="pt-meta">MiniJinja and CSS</p></div></div><p>One route tree, one asset directory, and no browser build step.</p><a href="/docs/getting-started">Open setup</a></article>
<section class="pt-empty"><h3>No retained release</h3><p class="pt-meta">A prior release appears after the first verified replacement.</p><a class="pt-button" data-variant="secondary" href="/docs/deployment">Read deployment</a></section>
</div>
</section>
<section id="genre-tables" class="pt-specimen" data-component-genre="lists-tables" aria-labelledby="genre-data">
<div class="pt-specimen-copy">
<h2 id="genre-data">Lists and tables</h2>
<p>Native structure carries captions, headers, and values. Horizontal scrolling protects narrow screens.</p>
<p class="pt-source">Ideas: <a href="https://github.com/carbon-design-system/carbon">Carbon</a> and <a href="https://github.com/primer/primer">Primer</a>.</p>
</div>
<div class="pt-demo pt-scroll-x">
<table class="pt-table">
<caption>Sample release files</caption>
<thead><tr><th scope="col">File</th><th scope="col">Bytes</th><th scope="col">State</th></tr></thead>
<tbody><tr><td><code>index.html</code></td><td class="pt-number">18,432</td><td>Verified</td></tr><tr><td><code>app.css</code></td><td class="pt-number">29,884</td><td>Verified</td></tr><tr><td><code>ponytail.css</code></td><td class="pt-number">27,901</td><td>Verified</td></tr></tbody>
</table>
</div>
</section>
<section class="pt-specimen" data-component-genre="identity-status" aria-labelledby="genre-identity">
<div class="pt-specimen-copy">
<h2 id="genre-identity">Badges, avatars, and status</h2>
<p>Compact metadata stays textual. Avatars are decorative when the adjacent name already identifies the person.</p>
<p class="pt-source">Ideas: <a href="https://github.com/primer/primer">Primer</a> and <a href="https://github.com/carbon-design-system/carbon">Carbon</a>.</p>
</div>
<div class="pt-demo pt-stack">
<div class="pt-cluster"><span class="pt-badge" data-tone="success">Ready</span><span class="pt-badge">Native</span><span class="pt-badge" data-tone="danger">Action required</span></div>
<div class="pt-person-row"><div class="pt-avatar-group" aria-hidden="true"><span class="pt-avatar">AJ</span><span class="pt-avatar">MS</span><span class="pt-avatar"><img src="/assets/forkbird-avatar.png" width="256" height="256" alt=""></span></div><div><strong>Release reviewers</strong><p class="pt-meta">Arna Jónsdóttir, Mateo Silva, and Forkbird</p></div></div>
</div>
</section>
<section class="pt-specimen" data-component-genre="commerce" aria-labelledby="genre-commerce">
<div class="pt-specimen-copy">
<h2 id="genre-commerce">Commerce and pricing</h2>
<p>Product, price, quantity, and action remain explicit. This specimen is labelled sample data.</p>
<p class="pt-source">Ideas: <a href="https://github.com/Shopify/polaris">Shopify Polaris</a>.</p>
</div>
<article class="pt-demo pt-card">
<p class="pt-meta">Sample catalog item</p>
<div class="pt-media-row"><span class="pt-avatar"><img src="/assets/forkbird-avatar.png" width="256" height="256" alt=""></span><div><h3>Bunfork field guide</h3><p>Printed operator reference</p></div></div>
<p class="pt-price"><data value="24">$24</data><span>example price</span></p>
<div class="pt-field"><label for="sample-quantity">Quantity</label><input id="sample-quantity" class="pt-input" type="number" min="1" max="9" value="1"></div>
<button class="pt-button" type="button">Add sample item</button>
</article>
</section>
<section class="pt-specimen" data-component-genre="authentication" aria-labelledby="genre-auth">
<div class="pt-specimen-copy">
<h2 id="genre-auth">Authentication and profile</h2>
<p>Autocomplete, input types, visible labels, and recovery links let browser features do useful work.</p>
<p class="pt-source">Ideas: <a href="https://github.com/material-components/material-web">Material Web</a> and <a href="https://github.com/microsoft/fluentui">Fluent UI</a>.</p>
</div>
<form class="pt-demo pt-stack" action="/examples/components" method="get">
<div class="pt-field"><label for="account-email">Email</label><input id="account-email" class="pt-input" type="email" name="email" autocomplete="email" placeholder="operator@example.com"></div>
<div class="pt-field"><label for="account-password">Password</label><input id="account-password" class="pt-input" type="password" name="password" autocomplete="current-password"></div>
<div class="pt-cluster"><button class="pt-button" type="submit">Sign in</button><a href="/docs/security">Review account security</a></div>
</form>
</section>
<section class="pt-specimen" data-component-genre="calendar-scheduling" aria-labelledby="genre-calendar">
<div class="pt-specimen-copy">
<h2 id="genre-calendar">Calendar and scheduling</h2>
<p>Date input uses the platform picker. The compact calendar uses time elements and a textual current-date state.</p>
<p class="pt-source">Ideas: <a href="https://github.com/microsoft/fluentui">Fluent UI</a> and <a href="https://github.com/material-components/material-web">Material Web</a>.</p>
</div>
<div class="pt-demo pt-stack">
<div class="pt-field"><label for="release-date">Release date</label><span class="pt-date-control"><input id="release-date" class="pt-input" type="date" value="2026-07-16"></span></div>
<div class="pt-calendar" aria-label="July 2026 sample week"><span>Mon</span><span>Tue</span><span>Wed</span><span>Thu</span><span>Fri</span><span>Sat</span><span>Sun</span><time datetime="2026-07-13">13</time><time datetime="2026-07-14">14</time><time datetime="2026-07-15">15</time><time datetime="2026-07-16" aria-current="date">16</time><time datetime="2026-07-17">17</time><time datetime="2026-07-18">18</time><time datetime="2026-07-19">19</time></div>
</div>
</section>
<section id="genre-activity" class="pt-specimen" data-component-genre="messaging-activity" aria-labelledby="genre-messaging">
<div class="pt-specimen-copy">
<h2 id="genre-messaging">Messaging and activity</h2>
<p>Activity is a readable list with actors, timestamps, and verbs. It does not pretend every update is a card.</p>
<p class="pt-source">Ideas: <a href="https://atlassian.design/components">Atlassian Design System</a> for concepts only, plus <a href="https://github.com/primer/primer">Primer</a>.</p>
</div>
<div class="pt-demo" aria-label="Sample release activity">
<div class="pt-activity-row"><span class="pt-avatar" aria-hidden="true">AJ</span><div><strong>Arna verified the manifest</strong><p class="pt-meta"><time datetime="2026-07-11T19:12:00Z">19:12 UTC</time></p></div></div>
<div class="pt-activity-row"><span class="pt-avatar" aria-hidden="true">MS</span><div><strong>Mateo reviewed the recovery steps</strong><p class="pt-meta"><time datetime="2026-07-11T19:25:00Z">19:25 UTC</time></p></div></div>
<div class="pt-activity-row"><span class="pt-avatar" aria-hidden="true">FB</span><div><strong>Forkbird marked the release ready</strong><p class="pt-meta"><time datetime="2026-07-11T19:31:00Z">19:31 UTC</time></p></div></div>
</div>
</section>
<section class="pt-specimen" data-component-genre="metrics-steps-bulk" aria-labelledby="genre-operations">
<div class="pt-specimen-copy">
<h2 id="genre-operations">Metrics, steps, and bulk actions</h2>
<p>Sample metrics use tabular figures. Ordered progress and bulk selection preserve native form semantics.</p>
<p class="pt-source">Ideas: <a href="https://github.com/carbon-design-system/carbon">Carbon</a>, <a href="https://github.com/uswds/uswds">USWDS</a>, and <a href="https://github.com/Shopify/polaris">Polaris</a>.</p>
</div>
<form class="pt-demo pt-stack" action="/examples/components" method="get">
<p class="pt-meta">Sample operational data</p>
<dl class="pt-stat-list"><div><dt>Files</dt><dd>18</dd></div><div><dt>Checks</dt><dd>3</dd></div><div><dt>Warnings</dt><dd>1</dd></div></dl>
<ol class="pt-step-list"><li>Build</li><li aria-current="step">Verify</li><li>Publish</li></ol>
<fieldset class="pt-check-list"><legend class="pt-legend">Select artifacts</legend><label class="pt-check"><input type="checkbox" name="artifact" value="pages"> Native pages</label><label class="pt-check"><input type="checkbox" name="artifact" value="assets"> Public assets</label></fieldset>
<button class="pt-button" data-variant="secondary" type="submit">Verify selected</button>
</form>
</section>
</div>
</main>
{% endblock %}