{% extends "base.html" %}
{% from "_parts.html" import band, defs, prose %}
{% from "ui/data.html" import badge, card, code_block, item, item_list %}
{% from "ui/layout.html" import grid, page_header, stack %}
{#
The index: every macro in `src/fjkit/templates/ui/`, one row each.
It used to be section 14 of the Components page — one pre-formatted string,
ninety macros aligned with spaces, annotated with a `·` no legend explained.
Two problems, and splitting the page fixes only the first: it sat at the tail
of a page nobody scrolls to the end of, and the fact a reader most often wants
from an index — does this take a
`{% raw %}{% call %}{% endraw %}` block, and what goes inside it — was encoded
rather than stated.
So the annotation became a badge on the row, in words. The rows come from
`cheatsheet.py` already in this language, which is why nothing below chooses a
string: a page that assembled the phrases would be the one place the English
and Chinese indexes could describe different macros.
Components stays the page you read; this is the page you look things up in.
#}
{% set sections = [{"id": "reading", "title": "How to read this"}]
+ sheet.rail
+ [{"id": "shell", "title": "The shell"},
{"id": "globals", "title": "Globals"},
{"id": "htmx", "title": "htmx attributes"},
{"id": "missing", "title": "When it is not here"}] %}
{#
One row, three parts: the signature, the note under it, and — only when the
macro is called with one — the badge naming its block.
`item_list` rather than `table`, because Basecoat's `.table td` is
`white-space: nowrap`, which is right for a data table and wrong for a column
of sentences. `item` is the kit's answer for definitions, as its signature
comment says, and `clamp=false` grew for exactly this: a description that runs
past two lines.
Absence carries meaning here. A row with no badge takes no block, which is most
of them, and a column of em dashes would be noise on every screen.
#}
{% macro macro_list(file) %}
{% call card(file.name, padded=false) %}
{% call item_list() %}
{% for macro in file.macros %}
{% set signature %}{{ macro.call }}{% endset %}
{% set block %}{% if macro.block %}{{ badge(macro.block, variant="outline") }}{% endif %}{% endset %}
{{ item(signature, macro.note, actions=block, clamp=false) }}
{% endfor %}
{% endcall %}
{% endcall %}
{% endmacro %}
{#
The same row without the badge: a name, and one line on what it is for.
#}
{% macro name_list(heading, entries, description=none) %}
{% call card(heading, description, padded=false) %}
{% call item_list() %}
{% for entry in entries %}
{% set name %}{{ entry.name }}{% endset %}
{{ item(name, entry.note, clamp=false) }}
{% endfor %}
{% endcall %}
{% endcall %}
{% endmacro %}
{% block content %}
{% call stack(8) %}
{% set intro %}
Every macro the kit ships, grouped by what it is for, with the one question an index is asked
most often answered on the row itself: does this take a block, and what goes inside it. The
Components page is where you see them
running; this is where you look one up.
{% endset %}
{{ page_header(page.title, intro) }}
{# ============================================================== reading #}
{% set lead_reading %}What is on a row, and two rules that apply to every one of them.{% endset %}
{% call band("reading", "How to read this", lead_reading) %}
{% call prose() %}
A Jinja macro is called in one of two ways, and the name does not tell you which. A macro
with nothing inside it is printed. A macro that wraps something is called with
{% raw %}{% call %}{% endraw %}, and whatever sits between that and
{% raw %}{% endcall %}{% endraw %} is handed to the macro to place. Jinja calls
that the caller block, and every macro below that wants one carries a badge saying what goes
in it.
import it from{% endset %}
{% set c_block %}on the right of a row, and only on the rows that have one: this macro is called with {% raw %}{% call %}{% endraw %}, and the badge says what goes inside. No badge means print it with {% raw %}{{ }}{% endraw %}{% endset %}
{% set c_note %}what the signature does not say. Absent when the signature says all of it{% endset %}
{{ defs("What is on a row", [
("The signature", c_macro),
("A block badge", c_block),
("One line under it", c_note),
]) }}
{% set r_attrs %}anything you pass that is not in the signature becomes an attribute, with underscores turned into dashes: hx_post="/tasks" arrives as hx-post="/tasks". That is every hx-*, aria-* and data-*, on every macro{% endset %}
{% set r_variant %}variant, tone and category name a role — success, destructive, muted — never a colour. The colour is the token layer's, so a rebrand moves it once{% endset %}
{{ defs("Two rules, everywhere", [
("Extra keywords", r_attrs),
("Roles, not colours", r_variant),
]) }}
{% endcall %}
{% endcall %}
{# ============================================================== the macros #}
{% for group in sheet.groups %}
{% call band(group.id, group.title, group.lead) %}
{% for file in group.files %}{{ macro_list(file) }}{% endfor %}
{% endcall %}
{% endfor %}
{# ============================================================== shell #}
{% set lead_shell %}Blocks, not macros — the one file you extend rather than import.{% endset %}
{% call band("shell", "The shell", lead_shell) %}
{% call prose() %}
ui/shell.html carries the <head>, the theme flash-guard, the
asset links and the header/main/footer skeleton. An app extends it once, in its own
base.html, and fills the blocks it needs. Fill sidebar or
nav, never both: the shell lays the header out differently for each, and filling
both gives you two navigations.
Every one of these reaches the markup as an extra keyword on a macro:
{% raw %}{{ button("Refresh", hx_get="/tasks/board", hx_target="#board") }}{% endraw %}.
Underscores become dashes on the way through, and that is the whole integration: no macro has
an htmx-specific parameter, and none needs one. The full reference is
htmx.org/reference; what follows is what a fjkit
page uses.
{{ entry.call }}{% endset %}
{{ item(written, entry.note, clamp=false) }}
{% endfor %}
{% endcall %}
{% endcall %}
{% endfor %}
{% endcall %}
{# ============================================================== missing #}
{% set lead_missing %}The answer is a parameter, a keyword, or an issue — in that order.{% endset %}
{% call band("missing", "When it is not here", lead_missing) %}
{% call grid(cols=2, gap=4) %}
{{ defs("When a macro is missing", [
("check the parameters", "most requests are a keyword that already exists — size, tone, padded, align"),
("pass it through", "any extra keyword becomes an attribute, which covers every hx-*, aria-* and data-*"),
("fjkit eject badge", "take that one macro into your app to edit; every other macro in its file is re-exported from the kit and keeps its fixes"),
("open an issue", "a blocked class names the component that does not exist yet; that is the report"),
]) }}
{% set d_learn %}how the pieces fit, and what htmx sends{% endset %}
{% set d_components %}every macro running, with the knobs turned{% endset %}
{{ defs("Read next", [
("Learn", d_learn),
("Components", d_components),
("examples/fjkit-demo/", "these macros inside a working app, in the repo"),
("src/fjkit/static/src/fjkit.css", "the token table behind every variant"),
]) }}
{% endcall %}
{% endcall %}
{% endcall %}
{% endblock %}