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.
pip install chirp chirp-uifrom 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().
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.
| Surface | What chirp-ui gives you |
|---|---|
| Chirp apps | App shells, navigation, cards, forms, overlays, dashboard panels, and htmx-safe patterns |
| Admin screens | Fragment islands, confirm flows, polling, row actions, inline edit controls, and status displays |
| Data-heavy pages | Tables, pagination, metrics, timelines, trees, charts, descriptions, and resource indexes |
| Streaming UIs | SSE status, streaming blocks, copy buttons, model cards, suspense states, and retry affordances |
| Documentation | Page heroes, nav trees, params tables, signatures, code blocks, and index cards |
| Coding agents | chirp_ui.manifest.build_manifest() with real components, slots, params, classes, and tokens |
chirp-ui ships more than 300 registry-described components and primitives.
| Family | Examples |
|---|---|
| Layout | container, stack, cluster, grid, frame, block, split_layout, app_shell, workspace_shell |
| Controls | btn, icon_btn, button_group, segmented_control, row_actions, theme_toggle, copy_btn |
| Feedback | alert, badge, toast, spinner, skeleton, progress, empty_state, callout |
| Forms | text_field, select_field, checkbox_field, toggle_field, file_field, date_field, wizard_form |
| Navigation | tabs, route_tabs, breadcrumbs, navbar, sidebar, nav_tree, pagination, stepper |
| Data display | table, metric_card, stat, timeline, tree_view, calendar, avatar, description_list |
| Overlays | modal, drawer, tray, popover, tooltip, command_palette |
| Effects and ASCII | shimmer_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.
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"]) # 309The shipped manifest schema is chirpui-manifest@5. It is available as:
| Surface | How to read it |
|---|---|
| Python API | chirp_ui.load_manifest() or chirp_ui.manifest.build_manifest() |
| CLI | python -m chirp_ui.manifest --json |
| Package data | chirp_ui.MANIFEST_PATH |
| Docs build | site/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.
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.
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.
chirp-ui stays server-rendered by default. Interactive components are htmx- and Alpine-native where browser state is the right tool.
| Pattern | Components and docs |
|---|---|
| HTMX fragments | fragment_island, poll_trigger, oob, infinite_scroll, HTMX-PATTERNS.md |
| Alpine behavior | dropdown, modal, tray, tabs, theme_toggle, copy_btn, ALPINE-MAGICS.md |
| App shell swaps | app_shell, shell_frame, route_tabs, SHELL-TABS-CONTRACT.md |
| High-state islands | island_root, grid_state, wizard_state, upload_state, DND-FRAGMENT-ISLAND.md |
| Streaming | streaming_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.
You can use chirp-ui without Chirp by adding its loader to a Kida environment.
from kida import ChoiceLoader, Environment, FileSystemLoader
from chirp_ui import get_loader
env = Environment(
loader=ChoiceLoader([
FileSystemLoader("templates"),
get_loader(),
])
)In standalone setups, register equivalent filters/globals and serve
chirpui.css, chirpui.js, chirpui-alpine.js, themes, and pattern assets
from chirp_ui.static_path().
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 servechirp-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.
| Package | Requirement |
|---|---|
| Python | >=3.14 |
| Kida | kida-templates>=0.9.0 |
| Chirp | Optional, but recommended for use_chirp_ui(app) |
| Browser JS | Alpine 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.
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.pyThen 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.pygit clone https://github.com/lbliii/chirp-ui.git
cd chirp-ui
uv sync --group dev
uv run poe ci| Task | Command |
|---|---|
| Run tests | uv run pytest or uv run poe test |
| Type check | uv run ty check src/chirp_ui/ or uv run poe ty |
| Lint | uv run ruff check . or uv run poe lint |
| Build CSS | uv run poe build-css |
| Check manifest | uv run poe build-manifest-check |
| Full CI | uv run poe ci |
| Docs site | uv 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.
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.
chirp-ui is part of a pure-Python stack built for Python 3.14t free-threading.
| ᓚᘏᗢ | Bengal | Static site generator | Docs |
| ∿∿ | Purr | Content runtime | - |
| ⌁⌁ | Chirp | Web framework | Docs |
| ʘ | chirp-ui | Python-native UI components | Docs |
| =^..^= | Pounce | ASGI server | Docs |
| )彡 | Kida | Component template engine | Docs |
| ฅᨐฅ | Patitas | Markdown parser | Docs |
| ⌾⌾⌾ | Rosettes | Syntax highlighter | Docs |
| ᓃ‿ᓃ | Milo | Terminal UI framework | Docs |
Python-native. Free-threading ready. No npm required.
MIT