A list is the minimal container: a plain list-group-flush rendering of
sibling slugs, each as a hyperlink. No icons, no per-row description, no
markdown blurb — list pages stay simple text. Rendering lives in
src/content/list.rs.
If you want icons and descriptions in the index, use a board
instead. The two types share field shapes (name, desc, icon, content)
but differ in rendering.
<ul class="list-group list-group-flush" id="name">
<li class="list-group-item"><h5 class="mb-0">Policies</h5></li>
<li class="list-group-item"><a href="constitution/">Constitution</a></li>
...
</ul>
This is the original list-group-flush chrome. The only deliberate change
from the older <b>-wrapped title is the <h5 class="mb-0"> tag, so the
title typography lines up with board cards across pages. Beyond
that, list rendering stays simple — by design: full board-card chrome was
considered and explicitly not applied to lists.
The name of the list itself (from its Header) is shown as the first
<li>, even though the parent that linked to this list already displays the
same name. The list’s own desc and icon are not rendered on the list
page itself; they exist so a parent board /
board_group row can show this list with the right name,
icon, and one-line blurb.
{
"type": "list",
"name": {
"en": "Policies",
"ja": "規約",
"zh": "政策"
},
"desc": {
"en": "Rules and agreements that govern FDS — charter, copyright, administration, and membership.",
"ja": "FDSを定める規約・契約 — 基本法、著作権、管理、加入。",
"zh": "FDS 的规则与协议 —— 基本法、版权、管理与加入。"
},
"icon": "/images/svg/policy/",
"content": [
"constitution",
"default_copyright",
"admin",
"join",
"members"
]
}
| Field | Type | Required | Notes |
|---|---|---|---|
type | "list" | yes | Exact discriminator string. |
name | string or LangDict | yes | See Common: Header and LangDict. |
desc | string or LangDict | no | Stored but not rendered on the list page itself; used by a parent that references this list. |
icon | string | no | Same resolution rules as board; used by a parent that references this list. |
content | array of strings | yes | Sibling subdirectory slugs. |
content semanticsSame as board: a flat array of slug strings, each matching a
sibling subdirectory that contains its own ctx.json. Parsing goes through
the shared parse_slug_list helper, so the legacy {file: "slug"} shape
also works. New entries should always be plain strings.
List::read(path) reads ctx.json, builds a Header, and parses the
slug list.List::render opens the <ul>, writes the title <li>, then for each
slug:
ctx.json if present, falling back to
Header::fallback(slug) if missing or unparseable.<li><a href="slug/">name</a></li> using the child’s resolved
name. No icon, no description.The edit route supports, against a List:
rename(lang, new_name) — per-lang rename.set_icon(icon) — empty clears.set_desc(lang, desc) — empty clears that lang; emptying all langs drops
desc from disk.add_item(slug), remove_item(idx), move_item(from, to) — manipulate
the slug list.Because the list page itself does not render icon or desc, those fields
only matter when this list is referenced by a parent — but editing them is
still supported and useful for that purpose.
List::save() rebuilds ctx.json from scratch:
let mut ctx = object!({});
ctx.set("type", "list");
self.header.write_into(&mut ctx); // drops empty icon / desc
ctx.set("content", <slugs as JSON array>);
fop::write_json_file(ctx_path, &ctx);
Rebuilding from scratch is the only reliable way to drop a cleared optional
field. The comment on List::save() explicitly calls this out — without the
rebuild, a cleared icon, desc, or banner would linger on disk.
remove_item / move_item return Err(io::Error{InvalidInput}) for
out-of-range indices — same contract as board.content must be a string array. The shared parse_slug_list will
also accept {file: "slug"} objects, but the
board_group / board_list
section-object shape ({name, items}) will silently parse to empty here —
set type correctly so the right reader runs.desc / icon are not visible on the list page itself.
Editors confused that “I set a desc and nothing changed” should be pointed
at the parent container.<h5 class="mb-0">, matching board card
headings. Don’t add Markdown to name expecting it to compile — name is
treated as a plain string, only desc is markdown-compiled (and only on
pages where it is rendered, which is not this one).