Pure-Python template components with static validation for HTML, Markdown, terminal, and CI reports
Project description
)彡 Kida
Pure-Python components for HTML, Markdown, terminal output, and CI reports.
Kida gives Python templates a real component model: typed props, named slots, static call-site validation, scoped state, error boundaries, and free-threaded rendering on Python 3.14t. No JavaScript build step. No runtime dependencies.
Quick Start
pip install kida-templates
{% def card(title: str, variant: str = "default") %}
<article class="card card--{{ variant }}">
<h3>{{ title }}</h3>
{% if has_slot("header_actions") %}
<div class="actions">{% slot header_actions %}</div>
{% endif %}
<div class="body">{% slot %}</div>
</article>
{% enddef %}
{% call card("Settings", variant="elevated") %}
{% slot header_actions %}<button>Save</button>{% end %}
<p>Configure your preferences.</p>
{% endcall %}
from kida import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("templates/"))
template = env.get_template("page.html")
html = template.render(title="Hello")
Static Validation
Kida catches component mistakes before a user sees a page, report, or terminal screen.
{% def badge(count: int, label: str) %}
<span class="badge">{{ count }} {{ label }}</span>
{% enddef %}
{{ badge(count="five", lable="Messages") }}
kida check templates/ --strict --validate-calls
templates/dashboard.html:5: K-CMP-001: Call to 'badge' — unknown params: lable; missing required: label
templates/dashboard.html:5: K-CMP-002: type: badge() param 'count' expects int, got str ('five')
Validation catches unknown params, missing required params, and literal type mismatches at check time.
Use Kida For
| Surface | What Kida gives you |
|---|---|
| Web apps | Component templates for Flask, FastAPI, Django, Chirp, and Bengal |
| Static sites | Reusable layouts, slots, typed content components, and scoped state |
| CI reports | Markdown step summaries and PR comments from pytest, coverage, ruff, ty, and more |
| Terminal tools | ANSI-aware tables, badges, panels, dashboards, and progress output |
| Framework tooling | Template metadata, block rendering, component discovery, and dependency analysis |
Component Model
Kida brings frontend-style composition to ordinary Python templates.
| Feature | Syntax |
|---|---|
| Typed props | {% def card(title: str, count: int = 0) %} |
| Named slots | {% slot header %} / {% slot %} (default) |
| Conditional slots | has_slot("footer") |
| Scoped slots (data up) | {% slot row let:item=item %} |
| Slot forwarding | {% yield name %} |
| Context propagation | {% provide theme = "dark" %} / consume("theme") |
| Error boundaries | {% try %}...{% fallback error %}...{% endtry %} |
| Co-located styles | {% push "styles" %} / {% stack "styles" %} |
| Pattern matching | {% match status %}{% case "active" %}...{% endmatch %} |
| Block-scoped variables | {% set %} (scoped) / {% let %} (template-wide) / {% export %} |
Component Discovery
kida components templates/
# components/card.html
# def card(title: str, subtitle: str | None = None)
# slots: header_actions, footer
#
# components/button.html
# def button(label: str, variant: str = "primary")
# slots: (none)
#
# 2 component(s) found.
Introspection API
template = env.get_template("components/card.html")
meta = template.def_metadata()
card = meta["card"]
print(card.params) # (DefParamInfo(name='title', annotation='str', ...), ...)
print(card.slots) # ('header_actions', 'footer')
print(card.has_default_slot) # True
Render Surfaces
One template syntax can target HTML, terminal output, Markdown, and CI reports.
HTML
from kida import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("templates/"))
html = env.get_template("page.html").render(title="Hello")
Terminal
from kida.terminal import terminal_env
env = terminal_env()
template = env.from_string("""
{{ "Deploy Status" | bold | cyan }}
{{ hr(40) }}
{% for svc in services %}
{{ svc.name | pad(20) }}{{ svc.status | badge }}
{% endfor %}
""")
print(template.render(services=[
{"name": "api", "status": "pass"},
{"name": "worker", "status": "fail"},
]))
Markdown
from kida.markdown import markdown_env
env = markdown_env()
md = env.from_string("# {{ title }}\n\n{{ body }}").render(
title="Report", body="All tests passed."
)
CI Reports (GitHub Action)
Turn pytest, coverage, ruff, and other tool output into step summaries and PR comments.
- uses: lbliii/kida@v0
with:
template: pytest
data: results.xml
data-format: junit-xml
post-to: step-summary,pr-comment
Built-in templates for pytest, coverage, ruff, ty, jest, gotest, sarif, release notes, and AMP agent reports. Full action docs →
Designed For Python 3.14t
Kida does not rely on the GIL for correctness. Templates compile to immutable
Python code, render state lives in ContextVar, and environment mutation uses
copy-on-write patterns. Public APIs are safe under PYTHON_GIL=0 on
free-threaded Python 3.14t.
Why Kida?
| Traditional templates | Kida | |
|---|---|---|
| Typed parameters | Usually no | param: str | None |
| Named slots | Usually no | {% slot name %} |
| Scoped variables | Often leak or surprise | set is block-scoped |
| Context propagation | Prop drilling | provide / consume |
| Error boundaries | Rare | {% try %}...{% fallback %} |
| Component styles | Disconnected CSS files | {% push "styles" %} |
| Call-site validation | Runtime errors | Compile-time checks |
| Component discovery | Read every file | kida components CLI |
| Block rendering | Framework-specific | render_block() for HTMX partials |
| Streaming | Varies | render_stream() and render_stream_async() |
| Free-threading | Not usually designed for it | GIL-free on Python 3.14t |
Advanced Features
Template Inheritance
{# base.html #}
<!DOCTYPE html>
<html>
<body>{% block content %}{% endblock %}</body>
</html>
{# page.html #}
{% extends "base.html" %}
{% block content %}<h1>{{ title }}</h1>{% endblock %}
Regions (Parameterized Blocks)
{% region sidebar(current_path="/") %}
<nav>{{ current_path }}</nav>
{% endregion %}
{{ sidebar(current_path="/about") }}
Regions are blocks (for render_block()) and callables (for inline use). Ideal
for HTMX OOB swaps.
Pattern Matching & Null Safety
{% match status %}
{% case "active" %}Active{% case "pending" %}Pending{% case _ %}Unknown
{% endmatch %}
{{ user.nickname ?? user.name ?? "Anonymous" }}
{{ config?.database?.host }}
{{ data ?|> parse ?|> validate ?|> render }}
Streaming & Block Rendering
# Stream chunks as they render
for chunk in template.render_stream(items=large_list):
response.write(chunk)
# Render a single block (HTMX partials)
html = template.render_block("content", title="Hello")
# Compose layouts with pre-rendered blocks
html = layout.render_with_blocks({"content": inner_html}, title="Page")
Compile-Time Optimization
template = env.from_string(source, static_context={
"site": site_config, "settings": app_settings,
})
html = template.render(page_title="Home", items=page_items)
Pure filters can be evaluated at compile time, dead branches can be removed, and
small components with constant args can be inlined. Use
kida render template.html --explain to see active optimizations.
Framework Integration
# Flask
from kida.contrib.flask import KidaFlask
kida = KidaFlask(app)
# Starlette / FastAPI
from kida.contrib.starlette import KidaStarlette
templates = KidaStarlette(directory="templates")
# Django
TEMPLATES = [{"BACKEND": "kida.contrib.django.KidaDjango", ...}]
CLI
kida render template.txt --data context.json
kida check templates/ --validate-calls --a11y --typed
kida components templates/ --json
kida fmt templates/
kida extract templates/ -o messages.pot
Status
Kida is pre-1.0 and used by Bengal and Chirp. The API can still move, but the core design goals are stable: pure Python, static validation, render-surface parity, and free-threaded safety.
Upgrading
Moving from 0.6.x? See the Upgrade to 0.7 tutorial
for the strict_undefined=True migration patterns.
Moving from 0.7.x? See the Upgrade to 0.8 tutorial
for the Mapping behavior change in null-safe access (?. and ?[...]).
Moving from 0.8.x? See the 0.9 release notes
for the Markdown escaping and | safe trust-boundary changes.
The Bengal Ecosystem
Kida is part of a pure-Python stack built for 3.14t free-threading.
| ᓚᘏᗢ | Bengal | Static site generator | Docs |
| ∿∿ | Purr | Content runtime | — |
| ⌁⌁ | Chirp | Web framework | Docs |
| =^..^= | Pounce | ASGI server | Docs |
| )彡 | Kida | Component framework | Docs |
| ฅᨐฅ | Patitas | Markdown parser | Docs |
| ⌾⌾⌾ | Rosettes | Syntax highlighter | Docs |
| ᓃ‿ᓃ | Milo | Terminal UI framework | Docs |
License
MIT License — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kida_templates-0.9.0.tar.gz.
File metadata
- Download URL: kida_templates-0.9.0.tar.gz
- Upload date:
- Size: 601.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2ba0a8dc887a8b2a2ca1c0e94f3c17665afda61ba619f372195f777af83f37e
|
|
| MD5 |
c46f87c14ddd1906b53ec29a26a30541
|
|
| BLAKE2b-256 |
1736814b5f445180d25bb80d77daeee63e4c7048aededa3eaea5239ad4cc7866
|
Provenance
The following attestation bundles were made for kida_templates-0.9.0.tar.gz:
Publisher:
python-publish.yml on lbliii/kida
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kida_templates-0.9.0.tar.gz -
Subject digest:
e2ba0a8dc887a8b2a2ca1c0e94f3c17665afda61ba619f372195f777af83f37e - Sigstore transparency entry: 1500046863
- Sigstore integration time:
-
Permalink:
lbliii/kida@8d1b49938992e0ce403aef9d66b88b9ec8db77b1 -
Branch / Tag:
refs/tags/v0.9.0 - Owner: https://github.com/lbliii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@8d1b49938992e0ce403aef9d66b88b9ec8db77b1 -
Trigger Event:
release
-
Statement type:
File details
Details for the file kida_templates-0.9.0-py3-none-any.whl.
File metadata
- Download URL: kida_templates-0.9.0-py3-none-any.whl
- Upload date:
- Size: 417.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad53aa98317f990159c23e5b40f1ba5733f0911e272d2398e3f424f0a59b623e
|
|
| MD5 |
b57e1a0da97c9c686bbfd24169c9b655
|
|
| BLAKE2b-256 |
75bf65b8bcfab2205f0df06b4ac7f9fc2ddd3fff049fa0a03fec1c181a1ada40
|
Provenance
The following attestation bundles were made for kida_templates-0.9.0-py3-none-any.whl:
Publisher:
python-publish.yml on lbliii/kida
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
kida_templates-0.9.0-py3-none-any.whl -
Subject digest:
ad53aa98317f990159c23e5b40f1ba5733f0911e272d2398e3f424f0a59b623e - Sigstore transparency entry: 1500047804
- Sigstore integration time:
-
Permalink:
lbliii/kida@8d1b49938992e0ce403aef9d66b88b9ec8db77b1 -
Branch / Tag:
refs/tags/v0.9.0 - Owner: https://github.com/lbliii
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@8d1b49938992e0ce403aef9d66b88b9ec8db77b1 -
Trigger Event:
release
-
Statement type: