ʘ chirp-ui

June 18, 2026 · View on GitHub

PyPI version Tests Python 3.14+ License: MIT Status: Alpha

Python-native UI components for Chirp and Kida apps.

chirp-ui gives server-rendered Python apps a real component vocabulary: Kida macros, typed variants and sizes, registry-cited CSS classes, design tokens, htmx/Alpine interaction patterns, and an agent-groundable manifest. No Node pipeline. No utility-class vocabulary. No mystery CSS strings that your Python tests and tools cannot see.

Quick Start

pip install chirp chirp-ui
from chirp import App, AppConfig, use_chirp_ui

app = App(AppConfig(template_dir="templates", debug=True))
use_chirp_ui(app, prefix="/static")
{% from "chirpui/layout.html" import container, grid, block %}
{% from "chirpui/card.html" import card %}
{% from "chirpui/button.html" import btn %}

{% call container() %}
  {% call grid(cols=2) %}
    {% call block() %}
      {% call card(title="Pipeline", subtitle="Live status") %}
        <p>Builds, deploys, and background jobs in one swappable panel.</p>
        {{ btn("View details", href="/pipeline", variant="primary") }}
      {% endcall %}
    {% endcall %}
    {% call block() %}
      {% call card(title="Queue", subtitle="7 jobs waiting") %}
        <p>Rendered on the server, ready for htmx refreshes.</p>
      {% endcall %}
    {% endcall %}
  {% endcall %}
{% endcall %}

When you use Chirp, use_chirp_ui(app) registers the template loader, filters, static assets, debug-aware strict validation, Alpine runtime support, and Chirp-aware link/swap helpers. For standalone Kida usage, use chirp_ui.get_loader() and serve the files from chirp_ui.static_path().

What It Is

chirp-ui is the optional companion design system for Chirp, built on Kida. It is not the framework itself. It is one opinionated way to build Chirp apps with a polished default UI, predictable HTMX behavior, and components that stay inspectable from Python.

The central idea is simple: the component registry is the source of truth. Macros, CSS, docs, validation, and the shipped manifest are projections of that registry.

Use chirp-ui For

SurfaceWhat chirp-ui gives you
Chirp appsApp shells, navigation, cards, forms, overlays, dashboard panels, and htmx-safe patterns
Admin screensFragment islands, confirm flows, polling, row actions, inline edit controls, and status displays
Data-heavy pagesTables, pagination, metrics, timelines, trees, charts, descriptions, and resource indexes
Streaming UIsSSE status, streaming blocks, copy buttons, model cards, suspense states, and retry affordances
DocumentationPage heroes, nav trees, params tables, signatures, code blocks, and index cards
Coding agentschirp_ui.manifest.build_manifest() with real components, slots, params, classes, and tokens

Component Vocabulary

chirp-ui ships more than 300 registry-described components and primitives.

FamilyExamples
Layoutcontainer, stack, cluster, grid, frame, block, split_layout, app_shell, workspace_shell
Controlsbtn, icon_btn, button_group, segmented_control, row_actions, theme_toggle, copy_btn
Feedbackalert, badge, toast, spinner, skeleton, progress, empty_state, callout
Formstext_field, select_field, checkbox_field, toggle_field, file_field, date_field, wizard_form
Navigationtabs, route_tabs, breadcrumbs, navbar, sidebar, nav_tree, pagination, stepper
Data displaytable, metric_card, stat, timeline, tree_view, calendar, avatar, description_list
Overlaysmodal, drawer, tray, popover, tooltip, command_palette
Effects and ASCIIshimmer_button, glow_card, aurora, ascii_card, ascii_table, ascii_spinner

Composition primitives are macros, not utilities. Reach for stack(), cluster(), grid(), frame(), and block() instead of inventing spacing or display classes.

Registry And Manifest

The registry in chirp_ui.components.COMPONENTS describes every public block: variants, sizes, modifiers, BEM elements, slots, composed children, emitted classes, tokens, maturity, authoring hints, and runtime requirements.

from chirp_ui import load_manifest
from chirp_ui.components import design_system_report

manifest = load_manifest()
card = manifest["components"]["card"]
print(card["params"])
print(card["slots"])

report = design_system_report()
print(report["stats"]["total_components"])  # 309

The shipped manifest schema is chirpui-manifest@5. It is available as:

SurfaceHow to read it
Python APIchirp_ui.load_manifest() or chirp_ui.manifest.build_manifest()
CLIpython -m chirp_ui.manifest --json
Package datachirp_ui.MANIFEST_PATH
Docs buildsite/public/chirpui.manifest.json from poe docs-build-all

This is the agent contract: downstream tools should cite the manifest instead of guessing component names, slots, variants, or CSS classes.

Framework Integration Metadata

Frameworks and static-site generators should use Chirp UI's package contract instead of hardcoding shipped asset names or filesystem paths:

import chirp_ui

contract = chirp_ui.get_library_contract()

print(contract.static_root)
print([asset.path for asset in contract.css])
print([asset.path for asset in contract.js])
print([pack.path for pack in contract.theme_packs])

The contract is framework-neutral. It describes the template package/path, static root, manifest path/schema, ordered CSS entries, ordered JS entries, and optional runtime assets. It also lists packaged token-only theme packs under contract.theme_packs. Hosts still own how those assets are bundled, linked, fingerprinted, and served.

CSS Contract

chirp-ui CSS is generated from partials and guarded by registry parity tests. Every shipped chirpui-* class must be cited by a registry entry, defined in CSS, and reachable from the templates that emit it.

The cascade order is public API:

@layer chirpui.reset, chirpui.token, chirpui.base, chirpui.component, chirpui.utility, app.overrides;

Put application overrides in app.overrides and use --chirpui-* custom properties for theming. Do not fight the design system with specificity.

:root {
  --chirpui-accent: #2563eb;
  --chirpui-container-max: 80rem;
  --chirpui-radius-lg: 0.75rem;
}

@layer app.overrides {
  .billing-panel {
    --chirpui-card-hover-border: color-mix(in oklab, var(--chirpui-accent) 35%, var(--chirpui-border));
  }
}

Fresh apps should start with a token-only app theme layer loaded after chirpui.css. Chirp-UI ships a starter at /static/themes/app-theme-starter.css, plus curated catalog packs at /static/themes/atlas.css, /static/themes/ember.css, and /static/themes/sage.css; each covers light, dark, and system mode so theme_toggle() has coherent app-owned tokens immediately. The live component showcase includes a /theme-packs matrix for visual comparison.

For token and override details, see APP-THEME.md, TOKENS.md, CSS-OVERRIDE-SURFACE.md, and COMPONENT-OPTIONS.md.

High-traffic components also support descriptor-backed visual presets through macro parameters such as appearance="outlined" and tone="danger". Use these instead of hand-written preset classes. See APPEARANCE-TONE.md for the pilot vocabulary and migration map.

Interactivity

chirp-ui stays server-rendered by default. Interactive components are htmx- and Alpine-native where browser state is the right tool.

PatternComponents and docs
HTMX fragmentsfragment_island, poll_trigger, oob, infinite_scroll, HTMX-PATTERNS.md
Alpine behaviordropdown, modal, tray, tabs, theme_toggle, copy_btn, ALPINE-MAGICS.md
App shell swapsapp_shell, shell_frame, route_tabs, SHELL-TABS-CONTRACT.md
High-state islandsisland_root, grid_state, wizard_state, upload_state, DND-FRAGMENT-ISLAND.md
Streamingstreaming_block, sse_status, model_card, copy_btn, HTMX-ADVANCEMENT.md

Named Alpine controllers live in chirpui-alpine.js and register through Alpine.safeData(), so htmx swaps can initialize behavior safely. Component templates do not ship inline <script> tags.

Standalone Kida (without Chirp)

chirp-ui works in Flask, FastAPI, Django, or bare kida — Chirp is optional. The only hard dependency is kida-templates; filters like bem and field_errors ship in chirp-ui itself via register_filters().

Start here: Standalone core guide — kida bootstrap, static assets, the Alpine.safeData shim, CSRF bridge, and dev checklist. See capability matrix for an honest Chirp vs hand-roll comparison.

Per-framework guides: Flask, FastAPI, Django.

from kida import ChoiceLoader, Environment, FileSystemLoader
from chirp_ui import get_loader, register_filters, static_path

# See docs/integration/standalone-core.md for the full bootstrap + Alpine wiring.
env = Environment(
    loader=ChoiceLoader([
        FileSystemLoader("templates"),
        get_loader(),
    ])
)

Bengal Theme (chirp-theme)

The same package ships chirp-theme, a static-first Bengal theme that puts the chirp-ui design language on documentation and marketing sites. Installing chirp-ui registers the theme through the bengal.themes entry point — no separate install — and you adopt it by pointing your site config at it:

# config/_default/theme.yaml  (or the theme block in bengal.toml)
theme:
  name: "chirp-theme"
uv run bengal build
uv run bengal serve

chirp-theme requires Bengal >=0.3.3, whose library_asset_tags() hook links the bundled chirpui.css. On older Bengal the base CSS is silently dropped and layouts collapse.

Full adoption quickstart: Apply chirp-theme to a Bengal site and the chirp-theme reference.

Requirements

PackageRequirement
Python>=3.14
Kidakida-templates>=0.9.0
ChirpOptional, but recommended for use_chirp_ui(app)
Browser JSAlpine 3.x for interactive components; auto-injected by Chirp integration

chirp-ui declares free-threading support and avoids build/runtime dependencies that rely on the GIL. The build pipeline is Python-native and CSS is assembled from package partials.

Showcase

The live, interactive showcase runs the real examples/component-showcase Chirp app — every macro, shell, form, island, and streaming demo against a real server runtime:

https://chirp-ui-showcase-production.up.railway.app

Or run it locally:

git clone https://github.com/lbliii/chirp-ui.git
cd chirp-ui
pip install -e ".[showcase]"
python examples/component-showcase/app.py

Then open http://localhost:8000.

With the b-stack workspace:

cd /path/to/b-stack
uv sync
uv run python chirp-ui/examples/component-showcase/app.py

Development

git clone https://github.com/lbliii/chirp-ui.git
cd chirp-ui
uv sync --group dev
uv run poe ci
TaskCommand
Run testsuv run pytest or uv run poe test
Type checkuv run ty check src/chirp_ui/ or uv run poe ty
Lintuv run ruff check . or uv run poe lint
Build CSSuv run poe build-css
Check manifestuv run poe build-manifest-check
Full CIuv run poe ci
Docs siteuv sync --group docs then uv run poe docs-build-all

If you edit CSS, change src/chirp_ui/templates/css/partials/*.css, run uv run poe build-css, and commit the generated chirpui.css.

If you edit a macro's public surface, update the registry entry and regenerate the manifest/docs projections.

Status

chirp-ui is pre-1.0 and shipped. Core principles are stable: Python-native components, registry-cited CSS, no utility vocabulary, htmx/Alpine defaults, and free-threading-ready tooling. Some variants, experimental effects, and legacy compatibility classes can still move before 1.0.

The Bengal Ecosystem

chirp-ui is part of a pure-Python stack built for Python 3.14t free-threading.

ᓚᘏᗢBengalStatic site generatorDocs
∿∿PurrContent runtime-
⌁⌁ChirpWeb frameworkDocs
ʘchirp-uiPython-native UI componentsDocs
=^..^=PounceASGI serverDocs
)彡KidaComponent template engineDocs
ฅᨐฅPatitasMarkdown parserDocs
⌾⌾⌾RosettesSyntax highlighterDocs
ᓃ‿ᓃMiloTerminal UI frameworkDocs

Python-native. Free-threading ready. No npm required.

License

MIT