)彡 Kida

May 30, 2026 · View on GitHub

PyPI version Build Status Python 3.14+ License: MIT

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

SurfaceWhat Kida gives you
Web appsComponent templates for Flask, FastAPI, Django, Chirp, and Bengal
Static sitesReusable layouts, slots, typed content components, and scoped state
CI reportsMarkdown step summaries and PR comments from pytest, coverage, ruff, ty, and more
Terminal toolsANSI-aware tables, badges, panels, dashboards, and progress output
Framework toolingTemplate metadata, block rendering, component discovery, and dependency analysis

Component Model

Kida brings frontend-style composition to ordinary Python templates.

FeatureSyntax
Typed props{% def card(title: str, count: int = 0) %}
Named slots{% slot header %} / {% slot %} (default)
Conditional slotshas_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 templatesKida
Typed parametersUsually noparam: str | None
Named slotsUsually no{% slot name %}
Scoped variablesOften leak or surpriseset is block-scoped
Context propagationProp drillingprovide / consume
Error boundariesRare{% try %}...{% fallback %}
Component stylesDisconnected CSS files{% push "styles" %}
Call-site validationRuntime errorsCompile-time checks
Component discoveryRead every filekida components CLI
Block renderingFramework-specificrender_block() for HTMX partials
StreamingVariesrender_stream() and render_stream_async()
Free-threadingNot usually designed for itGIL-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.

Moving from 0.9.x? See the 0.10 release notes for structured diagnostics and source-attribution hardening.

The Bengal Ecosystem

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

ᓚᘏᗢBengalStatic site generatorDocs
∿∿PurrContent runtime
⌁⌁ChirpWeb frameworkDocs
=^..^=PounceASGI serverDocs
)彡KidaComponent frameworkDocs
ฅᨐฅPatitasMarkdown parserDocs
⌾⌾⌾RosettesSyntax highlighterDocs
ᓃ‿ᓃMiloTerminal UI frameworkDocs

License

MIT License — see LICENSE for details.