Skip to content

lbliii/chirp-ui

Repository files navigation

chirp-ui

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

An optional, opinionated UI layer for Chirp — gorgeous by default, htmx-native.

pip install chirp chirp-ui
{% from "chirpui/layout.html" import container, grid, block %}
{% from "chirpui/card.html" import card %}

{% call container() %}
    {% call grid(cols=2) %}
        {% call block() %}{% call card(title="Hello") %}<p>Card one.</p>{% end %}{% end %}
        {% call block() %}{% call card(title="World") %}<p>Card two.</p>{% end %}{% end %}
    {% end %}
{% end %}

What is chirp-ui?

chirp-ui is an optional companion design system for Chirp. It provides Kida template macros — cards, modals, forms, layouts — that render as HTML. It is one polished, opinionated way to build Chirp apps, not the framework itself and not the only way to use Chirp. Use it with htmx for swaps, SSE for streaming, and View Transitions for polish. No client-side framework required for layout.

What's good about it:

  • Gorgeous by default — Full visual design out of the box. Override --chirpui-* CSS variables to customize.
  • htmx-native — Interactive components use htmx or Alpine.js. Dropdown, modal, tray, tabs, theme toggle, copy button use Alpine for declarative behavior.
  • Composable{% slot %} for content injection. Components nest freely.
  • Modern CSS:has(), container queries, fluid typography, prefers-color-scheme dark mode.

Installation

# pip
pip install chirp-ui

# uv
uv add chirp-ui

Requires Python 3.14+. When used with Chirp, components are auto-detected — no configuration needed.

You do not need chirp-ui to use Chirp. Use it when you want the companion component library, default design language, and app-shell patterns.

Version compatibility:

chirp-ui Kida Python Chirp (optional)
0.2.x >= 0.2.8 >= 3.14 >= 0.1.6 for use_chirp_ui

What's stable: Core macros (layout, card, modal, forms, alert, badge), filters (bem, html_attrs, validate_variant, validate_size), VARIANT_REGISTRY/SIZE_REGISTRY, static path, get_loader, register_filters. See SECURITY.md for | safe usage.

What may change: New component variants/sizes, API surface of __all__-excluded internals, Alpine.js migration patterns.


Quick Start

Step Action
1 Install Chirp and chirp-ui if you want the companion UI layer: pip install chirp chirp-ui
2 Serve static assets from the package (CSS, themes)
3 Import macros in templates: {% from "chirpui/card.html" import card %}
4 Include CSS: <link rel="stylesheet" href="proxy.php?url=/static/chirpui.css">
5 For interactive components (dropdown, modal, tray, tabs, theme toggle): use chirpui/app_shell_layout.html or chirpui/app_layout.html — both include Alpine.js

Serve assets:

from chirp.middleware.static import StaticFiles
import chirp_ui

app.add_middleware(StaticFiles(
    directory=str(chirp_ui.static_path()),
    prefix="/static"
))

Features

Feature Description
Layout container, grid, stack, block, page_header, section_header, divider, breadcrumbs, navbar, navbar_end, navbar_dropdown, sidebar, hero, surface, callout
UI card, card_header, modal, drawer, tabs, accordion, dropdown, popover, toast, table, pagination, alert, button_group, island_root, state primitives
Forms text_field, password_field, textarea_field, select_field, checkbox_field, toggle_field, radio_field, file_field, date_field, csrf_hidden, form_actions, login_form, signup_form
Data display badge, spinner, skeleton, progress, description_list, timeline, tree_view, calendar
Dashboard inline_edit_field, row_actions, status_with_hint, entity_header, confirm_dialog, confirm_trigger, fragment_island, poll_trigger — DASHBOARD-MATURITY-CONTRACT
Docs page_hero, nav_tree, params_table, signature, index_card — framework-agnostic docs components
Streaming streaming_block, copy_btn, model_card — for htmx SSE and LLM UIs
Theming --chirpui-* CSS variables, dark mode, optional Holy Light theme
Component options COMPONENT-OPTIONS.md — valid variants, sizes, strict mode

Usage

Component Showcase — Browse all components
pip install chirp chirp-ui
python examples/component-showcase/app.py

Open http://localhost:8000

Theming — Override CSS variables

chirp-ui uses prefers-color-scheme for dark mode. Override any --chirpui-* variable. Base colors drive derived states (hover, active, light, muted) via color-mix():

:root {
    --chirpui-accent: #7c3aed;
    --chirpui-container-max: 80rem;
    /* Optional: tune shade ratios for all colors */
    --chirpui-shade-hover: 85%;
    --chirpui-shade-muted: 15%;
}

Advanced tokens: HTTP methods (--chirpui-method-get, etc.), code syntax (--chirpui-code-keyword, etc.), secondary accent (--chirpui-accent-secondary, --chirpui-alert-secondary-*). Full token reference: PLAN-theme-tokens.md.

For manual light/dark toggle, set data-theme="light" or data-theme="dark" on <html>.

Optional theme: <link rel="stylesheet" href="proxy.php?url=/static/themes/holy-light.css">

Manual registration — Use with Kida without Chirp
from kida import ChoiceLoader, Environment, FileSystemLoader
from chirp_ui import get_loader

env = Environment(
    loader=ChoiceLoader([
        FileSystemLoader("templates"),
        get_loader(),
    ])
)

Call chirp_ui.register_filters(app) if using Chirp for form/field helpers.

Islands (framework-agnostic) — Isolate high-state widgets

chirp-ui stays server-rendered by default. For complex client-state widgets (editors, canvases, advanced grids), mount isolated islands on dedicated roots.

{% from "chirpui/islands.html" import island_root %}

{% call island_root("editor", props={"doc_id": doc.id}, mount_id="editor-root") %}
<p>Fallback editor UI (SSR) if JavaScript is unavailable.</p>
{% end %}

In Chirp, enable runtime lifecycle hooks:

from chirp import App, AppConfig

app = App(AppConfig(islands=True, islands_contract_strict=True))

Lifecycle events emitted in the browser:

  • chirp:island:mount
  • chirp:island:unmount
  • chirp:island:remount
  • chirp:island:state
  • chirp:island:action
  • chirp:island:error

For no-build defaults, use primitive wrappers from chirpui/state_primitives.html:

{% from "chirpui/state_primitives.html" import grid_state, wizard_state, upload_state %}

{% call grid_state("team_grid", ["name", "role"], mount_id="grid-root") %}
...
{% end %}

Included no-build primitives:

  • state_sync
  • action_queue
  • draft_store
  • error_boundary
  • grid_state
  • wizard_state
  • upload_state
SSE and streaming — htmx + Server-Sent Events
  • streaming_block — Use sse_swap_target=true for htmx SSE fragment swaps.
  • model_card — Use sse_connect=url, sse_streaming=true for LLM comparison UIs.
  • copy_btn — Copy button with data-copy-text. Enable AppConfig(delegation=True) for dynamically inserted buttons.

See Chirp RAG demo and LLM playground.

Dashboard refresh patterns — fragment islands, confirm flows, polling

For server-driven admin and settings pages, chirp-ui includes dashboard helpers that remove a lot of repeated htmx markup:

  • fragment_island(...) and fragment_island_with_result(...) for isolated refresh regions
  • confirm_dialog(...) and confirm_trigger(...) for confirmation flows
  • poll_trigger(url, target, delay=...) for hidden load/delay polling
{% from "chirpui/fragment_island.html" import poll_trigger %}

<div id="collections-results"></div>
{{ poll_trigger("/collections/status?refresh=1", "#collections-results", delay="1s") }}

These are especially useful in app-shell layouts where one panel refreshes in place while the rest of the shell stays stable.

Alpine Magics & Events

chirp-ui uses Alpine.js magics for accessibility and cross-component communication:

Magic Use
$el Current element (e.g. $el.dataset.label, $el.value)
$refs DOM refs for focus management (dropdown trigger/panel)
$store Global state (modals, trays) for overlay components
$id Unique IDs for ARIA (dropdown, tabs)
$watch Reactive sync (theme/style select)
$dispatch Custom events for app-level handling
$nextTick Post-render focus (dropdown_select)

Custom events — Listen for chirpui:* events on document or a parent:

Event When Detail
chirpui:dropdown-selected Dropdown item clicked { label, href? } or { label, action? } or { label, value? }
chirpui:tab-changed Tab clicked { tab }
chirpui:tray-closed Tray backdrop/close clicked { id }
chirpui:modal-closed Modal backdrop/close clicked { id }

Example: run HTMX or analytics when a dropdown item is selected:

document.addEventListener('chirpui:dropdown-selected', (e) => {
  if (e.detail.action) htmx.ajax('POST', '/api/action', { values: { action: e.detail.action } });
});

Full reference: docs/ALPINE-MAGICS.md

Icons and ergonomics

Many components support Unicode icons via the icon param:

{% from "chirpui/alert.html" import alert %}
{% from "chirpui/card.html" import card %}
{% from "chirpui/button.html" import btn %}
{% from "chirpui/callout.html" import callout %}

{% call alert(variant="warning", icon="⚠", title="Heads up") %}Body text{% end %}
{% call card(title="Feature", subtitle="Optional subtitle", icon="◆") %}Content{% end %}
{{ btn("Save", icon="✓") }}
{% call callout(icon="💡", title="Tip") %}Use Unicode for icons.{% end %}

For animated icons, use ascii_icon() in the component slot. For custom headers with actions, use the header_actions named slot (Kida 0.3+):

{% from "chirpui/card.html" import card %}
{% call card(title="Settings", icon="⚙") %}
{% slot header_actions %}
<button class="chirpui-btn chirpui-btn--ghost"></button>
{% end %}
<p>Body content.</p>
{% end %}

empty_state supports action_label and action_href for a primary CTA button.


Key Ideas

  • HTML over the wire. Components render as blocks for htmx swaps, SSE streams, and View Transitions. The server is the source of truth.
  • Companion, not core. chirp-ui is an optional layer on top of Chirp, not a requirement for using the framework.
  • CSS as the design language. Modern features (:has(), aspect-ratio, clamp()) used where they add value. All animations respect prefers-reduced-motion.
  • Composable. {% slot %} for content injection. Components nest freely. No wrapper classes.
  • Minimal dependency. kida-templates only. Chirp optional for auto-registration.

Requirements

  • Python >= 3.14
  • kida-templates >= 0.2.6

Interactive components (dropdown, modal, tray, tabs, theme toggle, copy button) require Alpine.js 3.x. The chirpui/app_shell_layout.html and chirpui/app_layout.html layouts include Alpine via CDN. For custom layouts, add Alpine before using these components.


Development

git clone https://github.com/lbliii/chirp-ui.git
cd chirp-ui
uv sync --group dev
pytest
Task Command
Run tests uv run pytest or poe test
Type check uv run ty check src/chirp_ui/ or poe ty
Lint uv run ruff check . or poe lint
Full CI poe ci

The Bengal Ecosystem

A structured reactive stack written in pure Python for 3.14t free-threading. Chirp is the framework; chirp-ui is one optional UI layer built on top of it.

ᓚᘏᗢ Bengal Static site generator Docs
∿∿ Purr Content runtime
⌁⌁ Chirp Web framework Docs
ʘ chirp-ui Optional companion UI layer ← You are here
=^..^= Pounce ASGI server Docs
)彡 Kida Template engine Docs
ฅᨐฅ Patitas Markdown parser Docs
⌾⌾⌾ Rosettes Syntax highlighter Docs

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


License

MIT

About

No description, website, or topics provided.

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors