Templates

Kida-powered terminal rendering with built-in form, field, help, and progress templates.

4 min read 747 words

Milo renders terminal UI through Kida templates — the same syntax you'd use for HTML, adapted for terminal output with ANSI colors and live rendering.

Template loader chain

Milo's template environment searches three locations in order:

flowchart LR U[Your Templates] --> B[Milo Built-ins] --> K[Kida Components]
from milo.templates import get_env

env = get_env()  # Pre-configured environment with all loaders
template = env.get_template("my_screen.kida")

Built-in templates

Template Description
form.kida Full form layout — iterates field specs and renders each field
field_text.kida Text/password input field with cursor
field_select.kida Select field with[x] / [ ]radio indicators
field_confirm.kida Yes/No confirm field
help.kida argparse help output styled with Kida
progress.kida Unicode progress bar ( / )

Tip

Override any built-in template by placing a file with the same name in your template directory. The loader chain checks your templates first.

Template auto-discovery

App.from_dir() finds a templates/directory relative to the calling file and sets up the loader chain automatically:

from milo import App

app = App.from_dir(
    __file__,
    template="dashboard.kida",
    reducer=reducer,
    initial_state=State(),
)
app.run()

This eliminates manual FileSystemLoader and get_env() setup. Pass templates_dir="views"to use a different directory name.

Built-in component macros

Milo ships reusable template macros incomponents/_defs.kida. Import them into any template:

{% from "components/_defs.kida" import header, section, key_hints, selectable_list %}
Macro Description
header(name, version, description) App banner with name, version, and description
header_box(name, version, description) Boxed header with rounded border
section(title, color) Colored section heading
status_line(level, message, detail) Icon + message line (success, error, warning, info)
kv_pair(label, value, width) Key-value pair with dot-fill alignment
kv_list(items, width) List of key-value pairs
def_list(items) Definition list (term + indented description)
example_block(examples) Description + command examples
tag_list(tags) Colored inline labels
breadcrumb(parts, sep) Hierarchical path display
command_row(name, description, aliases, tags) Command with aliases and tags
key_hints(hints) Keyboard shortcut bar
selectable_list(items, cursor, render, empty) Cursor-highlighted item list
scrollable_list(items, cursor, render, height, scroll_offset, empty) Viewport slice with overflow indicators
format_time(seconds) Seconds toMM:SS.CCformat

Selectable list

Renders a cursor-navigable list. Pass a render macro that takes(item, is_selected):

{% from "components/_defs.kida" import selectable_list %}

{% def render_todo(todo, is_selected) %}
{% if todo.done %}{{ icons.check | green }} {{ todo.text | dim }}
{% else %}{{ icons.cross | red }} {{ todo.text }}{% endif %}
{% enddef %}

{{ selectable_list(visible, state.cursor, render_todo) }}

Scrollable list

Likeselectable_listbut clips to a viewport height and shows "N more above/below" indicators:

{% from "components/_defs.kida" import scrollable_list %}

{{ scrollable_list(state.entries, state.cursor, render_entry,
                   height=15, scroll_offset=state.scroll_offset) }}

Format time

Converts seconds (float) toMM:SS.CC:

{% from "components/_defs.kida" import format_time %}

{{ format_time(state.elapsed) | bold | green }}

Writing templates

Templates use Kida syntax (similar to Jinja2). Your state dict becomes the template context:

{# dashboard.kida #}
{{ "Status Dashboard" | bold }}
{{ "=" * 40 | fg("dim") }}

{% for service in services %}
{{ service.name | ljust(20) }}{{ service.status | badge }}
{% end %}

Uptime: {{ uptime | duration }}

Live rendering

When running inside anApp, Milo uses Kida's LiveRendererfor flicker-free terminal updates. The renderer diffs the previous and next output and only redraws changed lines.

IfLiveRenderer is unavailable, Milo falls back to ANSI clear-screen writes (\033[2J\033[H).

Static HTML rendering

For one-shot output (documentation, exports), userender_html:

from milo import render_html

html = render_html(state={"count": 42}, template="counter.kida")

Note

render_htmlrenders the template once and returns the result as a string. It does not start an event loop or read keyboard input.