# Templates: Jinja → XML → image A screen template (the file a `screens:` entry points `template:` at) is a Jinja template. Rendering it produces a **string of XML**, which is then parsed and walked to build the image — so everything in a template must produce well-formed XML once Jinja's `{{ }}`/`{% %}` are evaluated. This has two practical consequences: attribute values coming from Jinja expressions need to end up quoted (`x="{{ x_pos }}"`, not `x={{ x_pos }}`), and any literal `&`/`<`/`>` in text content needs XML escaping (`&` for a literal `&`, as in the example below). ## Variables and functions available to every template | Name | Description | | --- | --- | | `width`, `height` | The panel size from the config's `screens:` section. | | `timestamp` | The current render time, as a timezone-aware `datetime` in the config's `settings.timezone`. | | `tomorrow` | `timestamp + timedelta(days=1)` — a convenience for "what's tomorrow's date", e.g. when a screen is designed to be read the evening before. | | `timezone` | `timestamp.tzinfo`, for constructing further `datetime`s. | | `source(name)` | Calls `.fetch()` on the named entry from the config's `sources:` section and returns whatever that data source produces (a list of calendar events, a weather forecast model, …). Fetches are not cached across calls — calling `source("weather")` twice in one render hits the API twice. | ## The tag vocabulary Every element in the rendered XML is a "view": either a layout container (``, ``, ``) or a piece of content (``, ``, ``, ``, and a few source-specific ones like ``). The full reference, with every attribute each one accepts, is in {doc}`../widgets/index`. The root element of every template must be ``. ## A worked example This is `configs/home/family_calendar.xml.jinja`, trimmed slightly: ```xml+jinja {% macro homework() -%} {% with ical = source("school_work") -%} {% for event in ical.walk('vevent') -%} {% set start = event.get('dtstart').dt -%} {% if start > timestamp.date() %} {{ start.strftime('%-d/%-m') }} {{ event.get('summary') }} {% endif %} {% endfor %} {% endwith %} {%- endmacro %} {{ timestamp.strftime('%A, %B %d') }} LÄXOR & PROV {{ homework() }} ``` A few things worth noting from this example, since they're easy to trip over: - `source("school_work")` is called inside a Jinja `{% for %}` loop, iterating events straight from the `icalendar` calendar object the `calendar.ical` source returns (see {doc}`../data-sources/calendar`). - A Jinja macro (`homework()`) that itself contains XML tags is a normal way to factor out a repeated chunk of layout — the macro's *output* is just more XML text, spliced into the surrounding document. - Nesting a `` of ``/`` items inside a `` composes layout containers just like composing HTML — see {doc}`../widgets/layout` for exactly how each one sizes and positions its children. - `width="{{ width }}"` on the `` pulls the panel width from the template variable, not a hardcoded pixel value, so the same template adapts if the config's `screens.width` changes. ## Debugging a template Two tools make it much faster to figure out why a screen doesn't look right: - `uv run todays_paper -s -d -o preview.png` — the `-d` flag logs the XML Jinja produced *before* it's walked into an image, which is the quickest way to spot a missing attribute, a mis-nested tag, or a macro that didn't expand the way you expected. - `-t "2024-12-25 08:00"` — render as if it were a specific date/time, useful for testing day-of-week/time-of-day branches in a template (like the homework-deadline check above) without waiting for that date to actually arrive.