Quirl
A reusable UI component library for Duck Framework's Lively reactive system.
Build consistent, themeable interfaces in pure Python — no separate frontend toolchain, no JavaScript bundling, no build steps for your UI logic.
What is quirl?
quirl provides a structured foundation for creating and documenting Duck Lively components. It handles theming, animation demos, and static documentation generation so you can focus on building interfaces, not boilerplate.
- Themeable by default — Design tokens flow through CSS custom properties; every component inherits from a shared theme without hardcoded values.
- Self-documenting — Components declare their own usage docs via markdown docstrings. A build script turns those into a static gallery.
- Animated demos without a server — Wrap any component in a Demo container to generate pure HTML/CSS/JS showcases. Deploy to GitHub Pages with zero hosting cost.
- No inheritance lock-in — The demo system works with any Duck component, built-in or third-party, without requiring base class changes.
Installation
pip install quirl
Requires Python 3.10+ and duckframework>=2.3.1.
Quick start
1. Define a theme
from quirl.theme import Theme
dark = Theme(
name="dark",
accent_color="#F5C842",
surface_color="#111318",
)
# Update the global theme
Theme.current = dark
2. Create a component
Inherit from Duck's built-in components. Write your docs as a markdown class docstring:
from duck.html.components.label import Label
class StatusLabel(Label):
"""
A themed label for displaying status text.
Usage:
```python
StatusLabel(text="Active", color="green")
```
Required Props:
- text: Display text
Optional Props:
- color: The text color for the label
"""
# Preview kwargs for the demo
docs_preview_kwargs = {"text": "Preview", "color": "#3b82f6"}
# Demo animation steps
docs_animation_steps = [
{"delay": 1000, "cursor": {"top": "50%", "left": "50%"}},
{"delay": 500, "target": "span", "click": True},
]
def on_create(self):
super().on_create()
# Update style
self.style.update({
"border-radius": "9999px",
"padding": "4px 12px",
"font-weight": "500",
})
3. Render a static demo
from duck.html.components.container import Container
from duck.html.components.button import Button
from duck.html.components.input import Input
from quirl.components import Demo
# Demo a built-in Button
button_demo = Demo(
component=Button(text="Submit", bg_color="blue", color="white"),
steps=[
{"delay": 1000, "cursor": {"target": "button"}},
{"delay": 500, "target": "button", "click": True},
{"delay": 2000},
],
title="Button",
)
# Demo a built-in Input
input_demo = Demo(
component=Input(type="text", name="email", placeholder="Email"),
steps=[
{"delay": 800, "cursor": {"top": "50%", "left": "30%"}},
{"delay": 1000, "target": "input", "click": True},
{"delay": 1500},
],
title="Input",
)
# Group components
container = Container(children=[button_demo, input_demo])
# Render to static HTML
html = container.render()
Deploy the output to GitHub Pages, Netlify, or any static host. No Python runtime required on the server.
Architecture
quirl is organized around three concerns: theming, documentation, and animation demos.
The theme system lives at the package root and provides extensible design tokens through a global class-level registry. Access the active theme anywhere via Theme.current.
The animation demo system lives under quirl.components.animation.demo and wraps any Duck component for static HTML output. It generates self-contained CSS animations and click simulations without a WebSocket connection or Python runtime on the client.
Development
quirl follows the Duck Framework code style:
- Google-style docstrings on every module, class, and method.
- Constructor-based initialization via kwargs only.
- No private-style method names (build_nav, not _build_nav).
- Components live in quirl/components/, themes and utilities at the package root.
License
See LICENSE.
Metadata
Release files for quirl 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| quirl-0.0.1.tar.gz | 23.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| quirl-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 52.9 kB
Release files / quirl-0.0.1.tar.gz
| Download URL | quirl-0.0.1.tar.gz |
|---|---|
| Size | 23.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f26f710ceb9c6fb113816c59e6ae7d0e94fc0e6a3898f5158221e3ae7e6150ba
|
|
BLAKE2b-256 checksum How to use checksums |
4469399c7b4750cbc68c6fe4c5aea5065f962f06c3c03e0b0670428c365bbce5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.
Transparency logRelease files / quirl-0.0.1-py3-none-any.whl
| Download URL | quirl-0.0.1-py3-none-any.whl |
|---|---|
| Size | 29.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
11197ad9a4ff22f90d65581bb378d36ca1b7a74a201031880b13adf4c23b83a1
|
|
BLAKE2b-256 checksum How to use checksums |
b2cbabb1d610b0cf16bdc063537b274e85abffe28806d45ff0d003adddaa0a23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.
Transparency log