Markdoc tag syntax specification

Version: 0.1.0 Draft
Author: Ryan Paul

Markdoc is a Markdown-based document format and a framework for content publishing. Markdoc extends Markdown with a custom syntax for tags and annotations, providing a way to tailor content to individual users and introduce interactive elements. This specification describes the syntax of Markdoc tags and how to parse them within Markdown content.

Note This specification is an early draft and is still largely a work in progress.

1Tags

TagEnd
%}

A Markdoc Tag is a piece of markup that applies custom behavior or formatting in a Markdoc document. Tags can be nested, making it possible to express hierarchy or apply custom formatting to enclosed children. Matched pairs of opening tags and closing tags signify the beginning and end of a tag element that encloses children.

Example № 1{% example %}
This paragraph is nested within a Markdoc tag.
{% /example %}

Tags can also be self-closing, containing no nested content:

Example № 2{% example /%}

The tag delimiters (TagStart and TagEnd) indicate the presence of a Markdoc tag within Markdown content. Characters within the delimiters are treated as the TagInterior. When a TagStart delimiter is detected in a Markdown document, the parser should scan forward until it finds the first TagEnd delimiter that is not enclosed within a ValueString in order to determine where the tag ends.

Determining what behavior and formatting is applied by a given Markdoc tag and its attributes is left to individual Markdoc implementations.

1.1Opening tag

An opening tag indicates the start of a Markdoc tag element that contains nested children. An opening tag’s TagInterior must include the name of the tag and can include zero or more tag attributes.

A tag may optionally have an unnamed PrimaryAttribute value following the Identifier:

Example № 3{% if $foo %}
This is a paragraph in an `if` tag.
{% /if %}

1.2Self-closing tag

A self-closing tag, indicated by a forward-slash at the end of the TagInterior, indicates a Markdoc tag element that does not contain nested children. A self-closing tag’s TagInterior must include the name of the tag and can include zero or more tag attributes.

1.3Closing tag

A closing tag, indicated by a forward-slash at the start of the TagInterior, indicates the end of a Markdoc tag element that contains nested children. A closing tag’s TagInterior may include include the name of the tag and optional trailing whitespace. A closing tag corresponds to the most recent opening tag that has the same tag name.

Note A future draft will specify the expected parsing behavior for malformed documents with mismatched opening and closing tags.

1.4Tag forms

Markdoc tags can be used as either block or inline elements in a Markdown document.

1.4.1Block form

A tag should be parsed as a block-level element when its opening and closing markers each appear on a line by themselves with no other characters except whitespace. In the following example, the tag foo should be parsed as a block-level element that contains a single paragraph:

Example № 4{% foo %}
This is content inside of a block-level tag
{% /foo %}

1.4.2Inline form

When the opening tag and closing tag appear on the same line within a paragrah, the tag should be treated as an inline document elmement nested inside of the block-level paragraph element:

Example № 5This is a paragraph {% foo %}that contains a tag{% /foo %}

When the opening tag and closing tag appear on the same line with no other surrounding content, the tag should still be treated as an inline document element, nested within an implied block-level paragraph element:

Example № 6{% foo %}This is content inside of an inline tag{% /foo %}

1.5Annotation

Annotation
TagBeginSpacelistoptAttributelistoptSpacelistoptTagEnd

An Annotation applies Attributes to the enclosing Markdown block. The attributes within the annotation are treated as though they are attributes on the document node itself. For example, an annotation can be used to add a CSS class to a heading node:

Example № 7# Heading {% .example %}

An Annotation may only be used as an inline document node. When an annotation appears on a line by itself, it is treated as though it is nested in a block-level paragraph element. Within an Annotation, each Attribute is separated by a space.

1.6Attributes

There are two types of attributes: full attributes (AttributeFull) and shorthand attributes (AttributeShorthand).

A full Attribute is a key-value pair that consists of an Identifier and a Value separated by an = sign. The Identifier serves as the Attribute‘s key. No whitespace is permitted between the tokens that make up an Attribute.

Example № 8{% foo="bar" baz=[1, 2, 3] %}

1.6.1Shorthand attribute

A shorthand attribute consists of a ShorthandSigil followed by an Identifier. The sigil represents the attribute’s key. The following table describes the attribute key represented by each sigil:

Sigil Key
# id
. class

A shorthand attribute is equivalent to a full attribute that uses the key represented by the sigil. The following examples produce the same output:

Example № 9{% #foo .bar %}
Example № 10{% id="foo" class="bar" %}

When there are multiple shorthand attributes that use the class sigil (.), the parser combines them into a single class attribute. The following examples are equivalent:

Example № 11{% .foo .bar .baz %}
Example № 12{% class="foo bar baz" %}

2Interpolation

Interpolation is used to insert a Markdoc variable or the return value of a Markdoc function into the text of the Markdown document. Interpolation can only be used inside of an inline document node. When an interpolation appears on a line by itself, it is implicitly nested as inline content within a paragraph.

Example № 13Hello {% $username %}

3Values

3.1Primitive values

3.1.1Null

ValueNull
null

A null value is represented with the keyword null.

3.1.2Boolean

ValueBoolean
true
false

3.1.3Number

Digit
/[0-9]/

3.1.4String

StringCharacter
any character"\

3.2Compound values

3.2.1Array

ArrayItem
ValueSpacelistopt,Spacelistopt

An array value (ValueArray) consists of a matched pair of square brackets containing a comma-delimited sequence of Values. A matched pair of square brackets that contains nothing or only whitespace is parsed as an empty array value. An optional trailing comma is permitted within non-empty arrays. Arrays may be nested to an infinite level of depth and may contain Markdoc Variables or Function invocations.

Example № 14{% foo=[1, false, ["bar", $baz]] %}

3.2.2Hash

A hash value (ValueHash) consists of a matched pair of curly braces containing a comma-delimited sequence of key-value pairs (HashKeyValue). A matched pair of curly braces that contains nothing or only whitespace is parsed as an empty hash value. An optional trailing comma is permitted within non-empty hashes. Hashes may be nested to an infinite level of depth and may contain Markdoc Variables or Function invocations as values. The HashKey may consist of either a bare identifier or a string surrounded by double quotes.

Example № 15{% foo={key: "example value", "quoted key": $variable} %}

3.3Variable

A Variable allows Markdoc content to incorporate an external value. Variables may be used for Interpolation or in place of a value in tag attributes. Variables consist of multiple segments, which are intended to support accessing a value that is deeply nested in a complex data structure. A Variable segment can be an identifier or a square-bracket enclosed value. Determining how to resolve a Variable into a value is left up to individual Markdoc implementations.

Example № 16{% foo=$bar.baz[10].qux %}
Note A future draft will specify the expected behavior of variables with the $ and @ sigils. Presently, the $ sigil should be treated as a conventional variable and the @ sigil is reserved for future use.

3.4Function

A function consists of an Identifier followed by FunctionParameters enclosed in parentheses. Functions are used to incorporate external logic in a Markdoc document.

A FunctionParameter may be either a Value or a key-value pair separated by an equals sign. Functions may be used for Interpolation or in place of a value in tag attributes. Function parameters may be any valid Value, including a Variable or another Function. Determining how to evaluate a Function is left up to individual Markdoc implementations.

Note A future draft will specify a default set of built-in functions that should be included in Markdoc implementations.

4Space

Space
Space (U+0020)
Horizontal Tab (U+0009)
New Line (U+000A)

5Identifier

Identifier
/[a-zA-Z]/IdentifierTaillistopt
IdentifierTail
/[-_a-zA-Z0-9]/

§Index

  1. Annotation
  2. ArrayItem
  3. ArrayItemWithOptionalComma
  4. Attribute
  5. AttributeFull
  6. AttributeItem
  7. AttributeShorthand
  8. closing tag
  9. CompoundValue
  10. Digit
  11. Fraction
  12. Function
  13. FunctionParameter
  14. FunctionParameterNamed
  15. FunctionParameters
  16. FunctionParameterTail
  17. HashItem
  18. HashItemWithOptionalComma
  19. HashKey
  20. HashKeyValue
  21. Identifier
  22. IdentifierTail
  23. Interpolation
  24. InterpolationValue
  25. opening tag
  26. PrimaryAttribute
  27. PrimitiveValue
  28. self-closing tag
  29. ShorthandSigil
  30. Space
  31. StringCharacter
  32. StringElement
  33. StringEscapeCharacter
  34. StringEscapeSequence
  35. Tag
  36. tag delimiters
  37. TagClose
  38. TagEnd
  39. TagInterior
  40. TagOpen
  41. TagSelfClosing
  42. TagStart
  43. Value
  44. ValueArray
  45. ValueBoolean
  46. ValueHash
  47. ValueNull
  48. ValueNumber
  49. ValueString
  50. Variable
  51. VariableSegmentValue
  52. VariableSigil
  53. VariableTail
  1. 1Tags
    1. 1.1Opening tag
    2. 1.2Self-closing tag
    3. 1.3Closing tag
    4. 1.4Tag forms
      1. 1.4.1Block form
      2. 1.4.2Inline form
    5. 1.5Annotation
    6. 1.6Attributes
      1. 1.6.1Shorthand attribute
  2. 2Interpolation
  3. 3Values
    1. 3.1Primitive values
      1. 3.1.1Null
      2. 3.1.2Boolean
      3. 3.1.3Number
      4. 3.1.4String
    2. 3.2Compound values
      1. 3.2.1Array
      2. 3.2.2Hash
    3. 3.3Variable
    4. 3.4Function
  4. 4Space
  5. 5Identifier
  6. §Index