Home: https://github.com/akingdom/llux-layout-markdown
Author: Andrew Kingdom on behalf of the Llux community
Version: 1.0 Draft 1
Status: Draft specification. Standardization and reference implementation work are planned.
This specification is written in the language it describes.
::: grid cols-2 gap-6 ::: main
Llux Layout Markdown is a meta-extension to CommonMark.
It adds two capabilities that Markdown lacks:
Both are provided through the same familiar ::: fence pattern.
::: flex gap-4
Left
Right
:::
The ::: delimiter is familiar from admonitions (:::info in MkDocs, Obsidian). Code blocks use triple backticks—a separate convention. Every CommonMark document remains valid. Every Llux document remains readable in any Markdown renderer. You only use ::: when you need meaning or layout.
You already know 80% of it. The other 20% solves problems you’ve been solving with hacks.
| User | Need |
|---|---|
| Writers | Footnotes, columns, pagination |
| Designers | Layouts sketched in plain text |
| Developers | Version-controlled, AI‑friendly prototypes |
| Governments | Standardised, accessible, multilingual documentation |
| Educators | Inclusive, interoperable learning materials |
| Enterprises | Documents that AI can understand |
| Anyone | Layout and meaning in plain text, without leaving Markdown |
Llux is not:
| System | Relationship to Llux |
|---|---|
| CommonMark | Llux is a superset. All CommonMark documents are valid. |
| Pandoc Fenced Divs | Similar concept; different syntax (::: vs :::div). Pandoc uses ::: {.class}; Llux uses ::: type attributes. |
| MyST Directives | MyST uses ::: with colon-separated namespaces. Llux uses a similar visual pattern. |
| Obsidian Admonitions | Obsidian uses :::info. Llux uses the same ::: opening, but requires a type (:::flex, :::grid). Plain ::: with no type is invalid. |
| VitePress/MkDocs | Both already support ::: admonitions. Llux extends this pattern to layout. |
All layout containers use the same pattern:
::: [type] [attributes]
content
:::
Syntax Requirements:
::: and the type.:::flex (without space) is invalid.```python).Colon count: A container is opened by ::: or more colons (::::, :::::, etc.). The exact number carries no semantic meaning—only the presence of at least three consecutive colons matters. This allows intuitive nesting without a formal hierarchy.
The parser looks at the first non-whitespace character after ::::
| First Character | Interpretation | Behaviour |
|---|---|---|
| Letter (a-z) | Container | Push frame |
! |
Intent comment | No frame |
[ |
Metadata key or key-value | No frame |
| Anything else | Invalid | Error |
Each ::: opening pushes a frame onto the stack. Each ::: closing pops the most recent frame.
::: flex ← Frame 1 (flex)
::: grid cols-2 ← Frame 2 (grid)
Card A
Card B
::: ← Pop Frame 2 (grid closes)
::: ← Pop Frame 1 (flex closes)
::: ! This is an intent comment. It will not be rendered.
Comments begin with ::: ! followed by text. There MUST be a space after ::: and before !. Comments are single-line only.
::: [timing] ← Key only (label/definition)
::: [duration](3.5s) ← Key-value pair
Rules:
::: [label] → Stores label as metadata on the parent container (no value).::: [label](value) → Stores label: value as metadata on the parent container.value can be any token (string, number, path, URL, JSON, etc.).) or other special characters, it should be encoded.Use Cases:
::: [duration](3.5s)::: [schedule](daily@9am)::: [event]::: [status](pending)::: [schema](person-schema.json)::: [config]({"key":"value"})--- BlockDocument metadata is delimited by --- at the top of the document.
Rules:
---.--- is optional and consumed.Identifiers:
| Identifier | Format | Example |
|---|---|---|
--- yaml |
YAML | title: My Document |
--- json |
JSON | {"title": "My Document"} |
--- toml |
TOML | title = "My Document" |
--- |
Defaults to YAML | title: My Document |
Example:
--- yaml
title: Llux Layout Markdown Specification
author: Andrew Kingdom
lang: en
design:
colors:
primary: "#3B82F6"
---
--- is metadata only at the top of the document. Anywhere else, it is a thematic break.
--- yaml
title: My Document
---
# Content
--- # Thematic break (not metadata)
More content.
::: flex gap-4
::: flex-1
First column
:::
::: flex-1
Second column
:::
:::
::: grid cols-3 gap-2
Card A
Card B
Card C
:::
Implicit items: If you omit nested ::: for items, each top-level block element (paragraph, heading, list, code block, blockquote) becomes a separate item.
::: flex gap-4
This is an implicit item (paragraph).
# This is an implicit item (heading).
- This is an implicit item (list).
:::
Containers nest naturally. Each ::: opens a scope; the matching ::: closes it.
::: grid cols-2
::: flex gap-2
Item 1
Item 2
:::
::: flex gap-2
Item 3
Item 4
:::
:::
When parsing ::: type [attributes]:
# → ID (only first applies). → CSS class (all accumulate)= → key-value attribute (last wins)Example:
::: card #user-profile .shadow-lg bg-muted p-4 lang="en"
carduser-profileshadow-lg, bg-muted, p-4{ lang: "en" }Whitespace in attributes: Quoted values (lang="en-US") may contain spaces. Unquoted values cannot.
::: spacer
:::
Empty containers are valid. They render as empty elements.
::: page new
# Chapter 2
:::
Additional attributes:
::: page new number=3 orientation=landscape
Containment: page can contain any layout container. Layout containers (flex, grid, sidebar) cannot contain page. If page appears inside a layout container, it breaks out.
::: chapter introduction
# Chapter 1: Introduction
:::
::: section background
## Background
:::
Semantic markers for table of contents and navigation.
::: sidebar width=1/3 bg-muted p-4
This is a sidebar note.
:::
::: callout type=warning
This is a warning callout.
:::
UI components use the same ::: fence pattern:
::: card title="User Profile" shadow hover-scale
This is card content.
:::
Mixed content: Cards may contain text and explicit items. Text before the first explicit item becomes an implicit item.
Interactive components (buttons, tabs, accordions) are extensions (see Appendix J). They are not part of the core specification.
Standard Markdown headings (#, ##, ###) are preserved. Heading hierarchy is required (WCAG 2.1 Success Criterion 1.3.1).
# Chapter 1
## Section 1.1
### Subsection
This is a sentence with a footnote.[^1]
[^1]: This is the footnote text.
By default: bottom of page (PDF) or end of document (HTML). Override with:
::: page new footnotes="bottom"
| Name | Age | Role |
|------|-----|------|
| Alice | 30 | Editor |
| Bob | 25 | Reviewer |
Caption: Team members
Caption appears above by default. Override with caption-position="bottom".
This text is [highlighted yellow]{bg-yellow-200} and [larger]{text-xl}.
Specificity: Inline styles override container-level utility classes, which override design tokens.
Accessibility is mandatory.
| Rule | Description |
|---|---|
| Heading hierarchy | No skipping levels |
| Image alt text | All images must have alt text |
| Table headers | All tables must have headers |
| Layout order | Must follow document order |
| ID uniqueness | All IDs must be unique |
| Custom type | May be used without declaration (warning) |
| Empty type | No plain ::: without a type |
| UTF-8 validity | Invalid UTF-8 sequences error |
Validation is mandatory and non-optional. If a document fails validation, the renderer MUST produce a clear error message with line number, and MUST NOT render.
Error: Heading level skipped (line 23)
Section 6.1 requires that heading hierarchy is logical.
Found: ### Subsection (line 23)
Expected: ## Section or higher.
A --force flag MAY be provided for emergency use, strongly discouraged.
::: grid cols-2 gap-4
::: lang-en
This is the English text.
:::
::: lang-fr
Ceci est le texte français.
:::
:::
::: lang-ar dir-rtl
النص العربي
:::
::: page
::: grid cols-2 gap-4
::: lang-en role-main
[English text]
:::
::: lang-fr role-translation
[French text]
:::
:::
:::
Defined in YAML frontmatter:
---
design:
colors:
primary: "#3B82F6"
background: "#F9FAFB"
muted: "#F3F4F6"
fonts:
heading: "Inter, sans-serif"
body: "Merriweather, serif"
spacing:
default: "1.5rem"
gap: "1rem"
---
::: flex gap-4 ::: flex-1 bg-muted p-4 rounded 1. Parser Reads Markdown; emits an AST with layout nodes. ::: ::: flex-1 bg-muted p-4 rounded 2. Transformer Applies design tokens; validates accessibility. ::: ::: flex-1 bg-muted p-4 rounded 3. Renderer Generates HTML, PDF, PNG, or SVG. ::: :::
Core types: page, chapter, section, flex, grid, item, sidebar, callout, card, figure.
Any other type is custom. Custom types may be used without declaration, producing a warning.
Warning: Undeclared custom type 'video' (line 12)
Consider adding 'video' to 'extensions' in frontmatter.
Using 'video' as a generic container.
::: acme/video src="..." :::
::: oss/slideshow interval="3" :::
Namespaces registered in a Git‑based registry. Open to all.
use Directive::: use video, slideshow :::
div with data-type.container = opening, content, closing
opening = <line_start>, ":::", whitespace, type, [whitespace, attributes], <line_end>
closing = <line_start>, ":::", [whitespace], <line_end>
type = [a-zA-Z][a-zA-Z0-9\-]* (with optional namespace: [a-z]+"/")
comment = <line_start>, ":::", whitespace, "!", [whitespace, text], <line_end>
key = <line_start>, ":::", whitespace, "[", text, "]", <line_end>
keyvalue = <line_start>, ":::", whitespace, "[", text, "]", "(", text, ")", <line_end>
Unicode Safety: Parser looks only for ASCII colons and whitespace. All other content passes through.
Line Start: Container opens with ::: at line start, after up to three spaces of indentation.
Precedence:
```). No lexical overlap.--- or *** on their own line. --- followed by text is literal.::: but require a type. Plain ::: with no type is invalid.^[ ]{0,3}:::$ (closing):
^[ ]{0,3}::: +[a-zA-Z] (opening):
extensions, warn (loose) or error (strict).^[ ]{0,3}::: +! (comment):
^[ ]{0,3}::: +\[ (key/key-value):
[label] or [label](value).frame = {
type: string,
attributes: object,
content: string,
line_start: number,
metadata: object // Stores comments, keys, key-values attached to this container
}
Opening: ^[ ]{0,3}:::[ \t]+([a-zA-Z][a-zA-Z0-9\-]*(?:\/[a-zA-Z][a-zA-Z0-9\-]*)?)
Closing: ^[ ]{0,3}:::[ \t]*$
Comment: ^[ ]{0,3}::: +!
Key: ^[ ]{0,3}::: +\[
::: page #executive-summary
container-1)See the [executive summary](#executive-summary).
Follow WAI-ARIA 1.2. Custom roles prefixed with x-.
| Container | Default Role |
|---|---|
flex, grid |
group |
sidebar |
complementary |
callout |
note |
card |
article |
figure |
figure |
page, chapter, section |
region |
The colon (:) is universally accessible:
| Layout | : Key |
Ease |
|---|---|---|
| US QWERTY | Shift + ; |
🟢 |
| UK QWERTY | Shift + ; |
🟢 |
| German QWERTZ | Shift + . |
🟢 |
| French AZERTY | Direct key | 🟢 Easiest |
| Spanish | Shift + . |
🟢 |
| Nordic | Shift + . |
🟢 |
| Japanese | Shift + ; |
🟢 |
No layout requires AltGr, dead keys, or other problematic mechanisms.
| Pattern | Purpose | Example |
|---|---|---|
gap-{n} |
Gap between items | gap-4 |
cols-{n} |
Grid columns | cols-3 |
flex-{n} |
Flex grow factor | flex-2 |
p-{n} |
Padding | p-4 |
bg-{color} |
Background | bg-muted |
rounded |
Rounded corners | rounded |
text-{size} |
Text size | text-xl |
shadow |
Box shadow | shadow |
hover-scale |
Hover scale | hover-scale |
justify-{value} |
Flex justify | justify-center |
text-center |
Text alignment | text-center |
w-{n} |
Width | w-full |
| Directive | Purpose | Example |
|---|---|---|
--- |
Document metadata | --- yaml |
::: flex |
Flex container | ::: flex gap-4 |
::: grid |
Grid container | ::: grid cols-3 |
::: page |
Page boundary | ::: page new |
::: chapter |
Chapter marker | ::: chapter introduction |
::: section |
Section marker | ::: section background |
::: sidebar |
Sidebar | ::: sidebar width=1/3 |
::: callout |
Highlighted block | ::: callout type=warning |
::: card |
Card component | ::: card title="Profile" |
::: figure |
Figure | ::: figure src="..." |
::: item |
Child item | ::: item flex-1 |
::: use |
Extension declaration | ::: use video, slideshow |
::: ! |
Intent comment | ::: ! TODO: Add figure |
::: |
Metadata key | ::: [timing] |
::: |
Metadata key-value | ::: [duration](3.5s) |
This document demonstrates every feature it specifies:
| Feature | Location |
|---|---|
--- yaml |
Document metadata (frontmatter) |
::: grid cols-2 |
Abstract and sidebar |
::: flex |
Architecture section |
::: page |
Page breaks between chapters |
::: chapter |
Major section markers |
::: section |
Sub-section containers |
::: sidebar |
At-a-glance summary |
::: with # IDs |
Every page has an ID |
lang attributes |
Multi-language section |
| Design tokens | Frontmatter |
| Utility classes | Throughout |
| Accessibility | Section 6 validates itself |
| Extensibility | Appendix A defines the mechanism |
If you can read this, you can write it.
The specification text follows the Llux documentation/specification terms. See ../LICENCE.md.
Reference implementation code is intended to use the MIT licence unless a file states otherwise.
The Llux Layout Markdown specification is governed by the Llux community—developers, users, and contributors to the Llux programming language project.
No single entity controls the specification. It belongs to the community.
This specification is provided “as is”, without warranty of any kind.
This specification originated within the Llux programming language project and is fully implemented in the Llux standard library. However, the specification is independent and can be implemented in any language.
The Llux implementation provides:
The specification stands alone and is not dependent on Llux.
This appendix defines optional UI component extensions. These are not part of the core specification.
::: button variant="primary" size="lg"
Click Me
:::
| Component | Purpose | Example |
|---|---|---|
button |
Clickable button | ::: button variant="primary" |
input |
Text input | ::: input type="text" |
select |
Dropdown | ::: select options="Option 1, Option 2" |
tabs |
Tabbed interface | ::: tabs |
accordion |
Expandable sections | ::: accordion |
form |
Form container | ::: form action="/submit" |
checkbox |
Checkbox | ::: checkbox label="Accept" |
radio |
Radio group | ::: radio group="options" |
| Attribute | Purpose | Example |
|---|---|---|
variant |
Visual style | variant="primary" |
size |
Size | size="lg" |
disabled |
Disabled state | disabled |
loading |
Loading state | loading |
onclick |
Click handler | onclick="submit()" |
type |
Input type | type="email" |
placeholder |
Input placeholder | placeholder="Enter email" |
options |
Select options | options="Option 1, Option 2" |
Implementations MAY render components as:
UI components are extensions. Implementations that support them MUST:
::: fence patternImplementations that do not support UI components SHOULD: