SYSTEM / CONTENT

Content format – MD page definitions

Updated 10/2026

The content of this site lives in Markdown files inside the repository – no database, no CMS, no admin interface. The binding shape is set by ADR-0011 (content schema) and ADR-0012 (language mutations). The build reads the files, validates them and renders static HTML. An unknown field, a missing required value or a typo in a control prefix stops the build, so the mistake cannot be published.

Where the content lives

  • content item – the directory content/ygg/explore/<slug>/: language-neutral metadata in item.md, the translation in cs.md and en.md
  • static page – content/ygg/pages/<slug>/cs.md and en.md
  • item assets – content/ygg/explore/<slug>/assets/

The directory is the slug, the file name is the language. Neither is written in the frontmatter – both are reported errors. A field that is not translated (type, dates, topics, links) belongs in item.md once for the whole item; a mutation carries only the translated fields and the body. A field in the wrong place is an error. Mutations are paired by directory: without en.md the English address simply does not exist. A translation is never faked with Czech text.

Item frontmatter

The required fields are type, topics, date and published in item.md, and title and tags in the mutation. The full list of fields with their place is in content/ygg/explore/FIELDS.md.

  • type – one of eight values guide, troubleshooting, reference, explainer, analysis, note, link, project. It drives the icon, the ordering and the address: a guide lives at /en/guide/<slug>/, a problem at /en/problem/<slug>/.
  • topics – a controlled vocabulary; a free keyword belongs in tags.
  • status – a controlled vocabulary shown in the item panel.
  • date, updated, verified, published, dataTo – always ISO YYYY-MM-DD. The MM/YYYY shape is a display format only, never a storage format.
  • published – the publication date orders the stream and sets the archive year; a future date means scheduled, false keeps the item out of the listings.
  • optionally summary, desc, hero, toc, numbering, scope, environment, difficulty, level, duration, reading, prereqs, sources, sourcesCount, related, callout, calloutLabel, url, source, from, stack, featured, order, assets, embeds, sections.
  • dev_only: true keeps an item in development only – out of the build, the site map, the feeds and search. An unfinished text is marked status: draft.
  • redirect_from is required as soon as a published address changes; the build turns it into a redirect.

The fields decide, not the record type: whatever is not filled in is not rendered. An empty value, null and an em dash — all mean the same thing – not filled in.

Item body

The body is Markdown extended by a closed set of control prefixes:

  • ## Heading is a section, ### Heading a subsection; the anchor comes from the heading slug
  • nav: a short section label for the side contents – allowed only right after a heading
  • command: a command line
  • note: and warning: a note and a warning
  • - a bullet, -! an accented bullet, 1. an ordered list
  • a fenced code block; the language output turns it into an output block rather than input
  • | key | value | is a two-column table; when the keys are dates it renders as a timeline
  • ![description](file.png) an image – the alt text is required, a decorative image does not belong in content

A prefix outside this list is an error, not text to render. The typo waring: would otherwise be quietly published as a paragraph.

Typography

Text is written with the characters on the keyboard: straight quotes and a hyphen. The build converts them by language – “…” in English, „…“ in Czech, a spaced hyphen between words into a dash and a year range into an unspaced dash. Code, commands, HTML tags and addresses stay unchanged. A typographic character typed by hand is a check error, so the source never carries two conventions.

Static page

A static page comes in two shapes and both are valid:

  • reading – like this page: the frontmatter carries kicker, title, optionally lead and updated, and the content is entirely in the body
  • structured – like About, Work or Public: the sections field in the frontmatter drives the order and shape of the bands, and long texts sit in the body behind a <!-- section: id --> marker

The section types are text, grid, list, index, states, steps, chips, keyvals, stat, kontakt, callout and featured-projects. The last one is never filled in by hand – it is derived from the project collection. Most sections keep their data in items; keyvals keeps it in groups and chips may add links below the badges. A section without its data would be an empty band, so validation rejects it. An internal target is addressed in a section by linkRoute (an id from the route table) and a list item by its slug; a finished path does not belong in content, because the route table owns it and the build adds the language.

The two shapes are never mixed. A heading in the body of a structured page would be a second, unmanaged structure next to sections, and validation rejects it.

Safety and checks

Raw HTML is limited to details, summary, figure, figcaption, abbr, mark, kbd, sub and sup, with the attribute open only on details and title only on abbr. Anything else is printed as text, not as markup.

Assets are recorded in the assets field with path, origin, creator and license; third-party material also with source_url. The path must be relative and free of ...

The whole tree is checked by make content-check – the same validation the build uses. An error is reported with the path to the field, not just the file name.