Skip to content

Component contract

The maintainer contract for OINK authoring primitives, validation, Book and release behavior, and output degradation.
OINK 1.3.1 contract

This contract describes OINK v1.3.1. Theme development uses the sibling checkout; release acceptance verifies the published module with replacements and workspaces disabled. Consumer adoption and deployment have their own checks.

Tutorials and exhaustive examples belong in the reader-facing Components section. This page defines the API and behavior that those guides rely on.

Authoring model

Use ordinary Markdown when one block plus attributes can express a component. Use shortcodes for compound bodies or facts Markdown cannot carry. There is no parallel component registry. Native forms require:

markup:
  goldmark:
    renderer: { unsafe: true }
    parser:
      wrapStandAloneImageWithinParagraph: false
      attribute: { block: true }

Only {{% steps %}} uses percent delimiters because its body belongs to the page outline; every other shortcode uses angle delimiters. Compound bodies pass through content/render-block.html with a unique ID scope. Shortcode and component parameter captions, labels, titles, and names are plain text; Markdown belongs in bodies. Landing narrative fields follow their own contract. An icon is one Font Awesome class pair. Components expose safe classes and attributes, not arbitrary color or inline style.

Public API

OINK has 29 shortcodes:

  • core: tabs, tab, steps, cards, card, fields, field, include, kbd, badge, param, comment, contributors, asciinema;
  • Book: fig, tbl, eq, eg, xref, book-toc, book-figures, book-tables, book-equations, book-examples;
  • release: release-card, release-assets, download;
  • OpenAPI: swagger, redoc.
Component Native form Shortcode form HTML runtime
Callout > [!TYPE], fold, {icon=} none none
Tabs adjacent fences/tables with {tab= group= value=} tabs / tab tabs on used pages
Steps ordered list + {.steps} steps none
Cards link list + {.cards} cards / card none
Fields table + {.fields} fields / field none
FileTree filetree data fence none divider only with comments
Gallery gallery data fence none shared Image Zoom when eligible
Image Markdown image + block attributes none Image Zoom when eligible
Table attributes, caption, number, or tabs tbl for compound Book tables tabs when tabbed
Book target image/table/passthrough/fence + {num=} fig, tbl, eq, eg none
Release assets checksums data fence release-assets copy in HTML
Math and chemistry passthrough, math, chem fences eq none; build-time rendering and local styles
Diagram/data mermaid, plantuml, markmap, echarts, infographic fences none selected local runtime only

Validation

Invalid author input follows the architecture contract: warn, use the documented safe fallback or omit the component, and let --panicOnWarning make the same diagnostic fatal at publication gates. Named and positional forms are not mixed. Book target IDs match [A-Za-z][A-Za-z0-9_.:-]*; Book numbers match [0-9A-Za-z.-]+; classes are token-validated. Hook and shortcode targets share one page registry, so collisions cannot produce duplicate output IDs.

URLs use content/url.html; raw backslashes are invalid because browsers may interpret them as URL separators. Images resolve through page resources, section resources, global assets, then static or explicit remote URLs. Local rasters carry intrinsic dimensions; SVG, static, and remote sources remain valid but cannot use Hugo image operations. Resource metadata alt must be a string; an invalid value warns and is ignored, preserving the image’s authored alt text.

Empty tabs warn and emit no container in HTML, Print, Markdown, or RSS. An xref without a usable target or with a missing target page preserves its text without a link. If fig supplies both caption and title, the warning and all outputs prefer caption. Generic attributes on block mathematics do not enable Book numbering. Without num, id and caption warn and are ignored; the formula stays unnumbered.

Component behavior

Callouts and tabs

Callout types are note, tip, important, warning, caution, success, danger, question, example, quote, and details. - makes the callout initially collapsed. + makes it expanded. Unknown types render as plain blockquotes, with their markers preserved and no JS.

Adjacent tabs group only when consecutive and of the same block kind. group enables the hash #<group>-<value> and storage td-tabs:v1:<group>. Ungrouped tabs use neither. HTML exposes all panels before JS. Print expands them. Markdown keeps authored source. RSS provides the rendered text summary. Grouped tab IDs and local links remain unchanged in HTML and single-page Print. Repeated groups and nested tab containers keep unique IDs within each page. Multi-page section and whole-Book Print prefix generated tab container and panel IDs with the source page namespace and update their local links. Reusing a group across chapters must not send a tab link to another chapter. Cross-page links to ordinary HTML keep their destination. Fragment rewriting accepts normal HTML attribute syntax and preserves text inside other attributes. The full form supports arbitrary Markdown. tab.label is required. value is required exactly when the parent has group. Orphan tab shortcodes warn and render nothing.

Steps, cards, fields, and tables

Native steps accept ordinary block content. Use the shortcode only when a step must contain a percent-delimited container. Native cards are link lists; the full form adds bodies, badges, icons, and images. Native fields map the first column to the name, the last to the description, and middle columns through meta= or headings; the full form allows block descriptions. card and field are valid only inside their parents.

Field anchors are field-<name> with lowercase punctuation runs collapsed to hyphens, so params.ui.typography becomes field-params-ui-typography. Duplicate anchors receive positional suffixes.

The table hook owns responsive wrapping and captions. .matrix makes the first column row headers; .full-width widens the outer wrapper to the article width. On screens, .td-table--wide opts technical tables into intrinsic sizing inside that scrollable wrapper, preserving identifiers with overflow-wrap: break-word and word-break: normal. Body cells have a soft max-width: 40ch bound so prose wraps. Ordinary tables retain their existing layout. The modifier has no effect on .fields, which renders a definition list, or on static and print output. .fields cannot combine with matrix, full-width, numbering, or tabs; numbering and tabs are also mutually exclusive. This width modifier is available in OINK 1.3.1.

The Markdown image hook is the ordinary image API. Inline images stay inline; block images become figures with caption or num. Image processing belongs to this native form alone: the full fig source form is a numbered container whose parameter list deliberately excludes command/options, so a processed numbered image is written as a native block image with num. Allowed image attributes are id, num, caption, width, height, link, command, and options plus shared safe attributes. command and options appear together and use Hugo Fit, Resize, Fill, or Crop on processable local resources. A plain linked image uses Markdown syntax; the link attribute therefore requires a caption or number. Linked and decorative images do not load Zoom.

Zoom triggers keep the image’s alt text and localized preview action in their ARIA accessible name, without inserting helper text into the article. Copying content as plain text or rich HTML must not add preview instructions, even when an editor discards the theme’s styles. Authored images and captions are preserved. When Draw.io and Image Zoom share an image, Edit and Zoom remain separate sibling buttons. The editor entry supports keyboard access and stays visible on touch devices and in forced-colors mode. The Draw.io editor opens in a modal dialog with a localized host Close button. The button, backdrop, editor exit message, and Escape while focus is in the host dialog dismiss it, release its message listener, and restore focus to Edit. Cross-origin iframe keystrokes belong to the editor and cannot bubble to the host; users can leave the editor’s Tab sequence to reach the host Close button. Opening another editor starts a fresh session.

Gallery accepts one Markdown image per line with optional description, link, and class. FileTree accepts indentation, - name, optional /, comments, and validated icon/tone/open/type attributes. FileTree fence wrap accepts true or false, defaults to false, and changes only screen comment layout: true wraps comments, including unbroken tokens, while rows grow from their existing minimum height. Filenames retain ellipsis. Missing or false values preserve compact desktop/tablet rows; mobile stacking, divider controls, directory folding, and expanded Print output keep their existing behavior. Invalid values warn and fall back to false; strict publishing rejects the warning. Markdown preserves explicit wrap values and tree source; Print renders expanded static figures and trees.

All code highlighting uses Chroma. Common fence attributes include title, copy, wrap, collapse, label, id, line options, tabs, and Book num/caption. Copy returns authored source. Mermaid palettes follow mode independently of visual presets. The default dark edge-label background is #404040 for AA text contrast; authored params.mermaid.themeVariables remain authoritative.

ECharts input is declarative JSON/YAML; callbacks use $fn:<name> from window.OinkEchartsFunctions, never embedded script execution.

Markmap node text on screens follows the article text color (--bs-body-color) via --markmap-text-color, dynamically adapting to light/dark mode changes, style presets, and restored user preferences to maintain contrast. Printing retains Markmap’s default dark text on the white canvas. This mapping is scoped to article content across both the wrapper and generated SVG, and can still be overridden by site CSS of equal specificity or SVG inline tokens. (Added in OINK 1.3.1.)

Mathematics uses Hugo’s build-time KaTeX output and local CSS, without a browser math runtime. HTML and Print use the vendored stylesheet and preserve MathML and authored TeX. Validate mathematics with the current Hugo baseline defined in the architecture contract. Markmap uses the matching vendored KaTeX runtime. On narrow screens, numbered-equation captions wrap within the reading column; a long caption must not widen the page.

Swagger and Redoc accept an HTTP(S) specification URL or a path rooted under static/; neither resolves page resources. Redoc treats leading and non-leading slashes equivalently and joins local paths to baseURL. Only HTML is interactive; Print, Markdown, and RSS render a static specification link.

Book

The book type extends the docs shell and follows the content tree or data/docs_nav.json. book_number, book_part, book_kind, and book_status are presentation metadata. They do not change Hugo publication state.

Numbered kinds are fig, tbl, eq, and eg. Their default ID is <kind>-<num>. eg requires a caption. eq without num is an unnumbered display formula. xref names exactly one kind with optional page/anchor, or an anchor with explicit text. Numbered examples have one framed body and caption.

Footnotes belong to the page document. Native numbered tables and fences keep them there. Shortcode bodies are separate Goldmark documents, so footnote references in tbl, eg, fig, card, tab, field, or include warn and remain literal. The check ignores text that has code syntax.

book-toc follows navigation order at depth 1–3. Each of the four book-* indexes collects one target kind. Single-page Print keeps ordinary heading, footnote, and tab IDs exactly as regular HTML renders them. Multi-page section and whole-Book Print rewrite cross-page links and namespace page-local headings, footnotes, and generated tab IDs to prevent collisions. Local heading links match after one URL-fragment decoding step, so ordinary Markdown links to Unicode headings retain their target in an aggregate. Explicit target IDs remain intact. Consumers opt into these potentially expensive aggregate outputs. Consumer-authored raw HTML anchors and custom shortcode IDs remain global by default. Add the boolean data-td-print-local attribute to an element with an id to declare a page-local anchor. An aggregate namespaces that ID and its same-page links; ordinary HTML and single-page Print retain the original ID. This lets legacy aliases and custom shortcode headings keep their existing deep links while remaining unique across aggregated chapters. Unmarked authored IDs must remain unique within their aggregate.

Release and download

Release front matter uses one release_url matching https://github.com/<owner>/<repo>/releases/tag/<tag>. The URL supplies owner, project, and tag. The page supplies the date. No remote release state is fetched. Removed release, release_products, and release_group_by_product warn with their replacements. They provide no compatibility path. The section index lists every page. It uses parsed project tag when available, otherwise the page title. For layout: releases, HTML keeps this release index; section Print aggregates the full release bodies with chapter-local IDs, in both development and production builds.

Checksums accept canonical lines or one source resource, never both. Filenames cannot be paths. HTML adds local copying. Static outputs show complete hashes.

Downloads use data/download/<key>.yaml. Channels are rolling or pinned. Only pinned URLs and commands interpolate ${version} and ${tag}. Before publication, rolling channels remain usable, while pinned channels show pending. Markdown renders the complete channel list. RSS omits the component.

Switching tabs remeasures collapsible code blocks in the newly visible panel. Their expand and collapse controls remain usable after fonts settle and when ResizeObserver is unavailable.

Verification

Shared output rules live in the architecture contract; exceptions are defined with their components above. Markdown and RSS set no browser runtime flags; Print retains only flags required by rendered print features. Source checks cover parameters, hook policy, runtime isolation, and migration; output checks compare HTML, print, Markdown, RSS, and LLMS goldens; browser tests cover interactive surfaces. Migration is documented in the migration contract.