Markus

Markdown for layout intent

GitHub-flavored Markdown with a small, semantic vocabulary for layout.

Markus keeps GitHub Flavored Markdown as the content language, then adds a handful of colon-fenced directives for the things Markdown was never meant to say: this is a pull quote, these are peer cards, these two ideas belong beside each other.

The source stays diffable. The renderer decides whether that pairing becomes two columns, a stacked phone layout, or a print spread.

Encode intent in the document. Leave grid tracks, type ramps, and hover states to the theme.

Markus design principle

Write this, get this

Every example in these docs shows the Markus source beside what it renders. This one is a callout and a metric inside a two-up pair.

Markus

:::two-up{ratio="2:1"}
:::column
### Ship the smaller model

It answers 94% of support questions on its own, at a tenth of the cost.

:::tip
Route the rest to the large model.
:::
:::

:::column
::metric{value="0.1" unit="x" label="Cost per answer" delta="-90%"}
:::
:::

Preview

Ship the smaller model

It answers 94% of support questions on its own, at a tenth of the cost.

Cost per answer
0.1x-90%

Get started to install Markus, or browse the feature index for every directive and Markdown feature.

A vocabulary small enough to remember

Pull quote

An editorial quotation with optional attribution, tone, and alignment.

Card grid

Peer ideas that should read as a set, not a numbered list pretending to be a layout.

Two-up

A conceptual pairing: contrast, before/after, claim and caveat.

What belongs in the source

  • The kind of block you mean
  • A short, validated attribute set
  • Nested Markdown, including other directives
  • Document metadata in YAML front matter

What belongs in the theme

  • Column counts that collapse on small screens
  • Typography and color
  • Print, EPUB, or email fallbacks
  • Component-level accessibility rules

Ratios on a three-column track

two-up ratios count parts across three columns: 2:1 is two parts plus one; 1:2 is one plus two. Blocks outside two-up span the full width.

Primary (2 parts)

The narrative, claim, or longer explanation belongs in the wider column.

Aside (1 part)

Metadata, a caveat, or a short counterpoint.

Lead (1 part)

A label, qualifier, or setup line.

Body (2 parts)

Material that should dominate when the second column carries the weight.

Full width (all three columns)

Outside two-up, ordinary blocks span the entire track. Inside a card-grid, set span=full on a card to do the same.

Metrics as first-class facts

Leaf directives cover facts that should be extractable later, not just styled.

CSS properties in the authoring API
0
Markdown baseline
GFM
License
MIT
Pipeline from Markus source through a parser and a validating registry to semantic HTML
Parse, validate, then render. Never regex-replace a directive after the fact.

Structured procedures

  1. 1. Encode intent in source

    Author content in GFM, wrapping layouts in colon-fenced directives.

  2. 2. Validate against strict schemas

    Markus checks directives and attributes at compile time, rejecting rogue CSS.

  3. 3. Render accessible HTML

    Output semantic markup ready for static hosting, responsive themes, or print.

Multi-platform instructions

Install into the current environment:

pip install anthus-markus

Install the markus command in its own environment:

pipx install anthus-markus

Add Markus to a uv project:

uv add anthus-markus

Project timeline

  1. Initial Concept

    Markus originated as a strict semantic layer for technical publishing.

  2. Core AST & Validation

    Implemented the markdown-it directive parser, Pydantic attribute validation, and early theme support.

  3. Rich Directives & Themes

    Added tabs, step lists, timelines, and 20+ accessible color palettes.

  4. Conversations, diagrams, and examples

    Added conversation transcripts, flow diagrams, bar charts, highlights, heading anchors, and side-by-side source examples.

Why not raw HTML?

Raw HTML is a compatibility hatch, not the authoring API. It names CSS classes, invites sanitizer fights, and cannot tell a renderer whether three boxes are a comparison, a feature set, or leftover layout. Markus keeps a registry: unknown names fail in CI instead of silently becoming untyped markup.