the Longform Markup Language

A markup language and transport format for HTML and XML fragments.

Language Intro

Longform is an easy to read markup and templating language that outputs to HTML and XML. A Longform document can be parsed to a complete document in the output format or as fragments to be used by an application as a source of markup when generating a document, or manipulating DOM in a browser environment.

Example Longform markup

header::
  hgroup::
    h1:: Longform Markup Language
    p::
      A markup and templating language for producing <b class=keyword>HTML</b>
      and <b class=keyword>XML</b> document fragments.
Result
<header>
  <hgroup>
    <h1>Longform Markup Language</h1>
    <p>
      A markup and templating language for producing <b class="keyword">HTML</b>
      and <b class="keyword">XML</b> document fragments.
    </p>
  </hgroup>
</header>

Longform can be used in most situations where Markdown might be used. It is similar to Markdown in that its markup can be light weight and easy to author documents with. It also benefits from being able to express any HTML markup without extensions or falling back to HTML.

Longform also supports directives that alter how a block of Longform or plain HTML / XML is processed. This specification formalizes directives which may be used in a browser environment using a the text/vnd.longform media type.

For example, in a Longform template the directives @allow-elements and @allow-attributes can be used to allow user defined markup to be used in a template while filtering out any exempt markup. User input markup in the Longform syntax will have sanitization rules applied by the Longform parser, while embedded HTML or XML content will require that an external sanitizer such as the Sanitizer API or a library like DOMPurify is configured to hook into the parser and apply the rules to the user defined content.

Longform template using sanitization directives

@template
##card
section.card.note-#{position}::
  header::
    h3:: #{title}
    
  @allow-attributes:: lang dir
  @allow-elements:: details[open name] summary h4 p strong em a[href target]
  div.card-content::
    ##{content}

Finally, the work in progress Longform parser is small and fast. Currently at 4kB when minified and gzipped and supporting enough features to render this webpage. The parser is likely to grow but is unlikely to reach near the size of Commonmark at 47.2kB or Marked at 12kB. The parser is also fast, as it can complete its job by building up the resulting HTML fragment strings line by line instead of with a two step process of constructing and abstract syntax tree and then forming the valid output markup.

Fragments

Longform's primary design goal is to output fragments of HTML into a form that can be merged with other sources and rendered into a complete document by another program. A Longform parser should output fragments so a client side application can select the server rendered fragments from the pristine DOM to recreate an internal representation of the original Longform documents as DOM nodes. The client side application then may re-use those fragments when it takes over rendering the page. Many Longform documents can be sourced for a single HTML page, and an application would need to recreate each document for client side use, typically this would be done by identifying the documents using a URI, and the individual fragments using the URI's fragment.

Further-more, on state change, if the client side runtime requires new content—it can make HTTP requests for those documents using the document's URI. Once the application has retrieved and parsed the Longform document, it may then use the fragment identifier of the URI to select the Longform fragment required by the client. This process has unique security concerns, parser implementors and client side application developers should get familiar with the security considerations section.

To allow the selection of rendered HTML fragments from the pristine DOM, a Longform parser will embed a HTML id or data attributes in the fragments and export additional meta information that must be made available to the client application. The client application must use the meta information to then select from the DOM. For example this meta information may be passed to the client in a <script type="application/json"> element as JSON with a HTML id the application knows to look for.

Some HTML documents will not use every fragment sourced from the Longform documents used when rendering a page, but the client may still require those missing fragments for state changes. Other fragments types, such as the text fragment which has no ability to embed attributes to select them by, and templates which are modified when used in the HTML, cannot be sourced from the DOM. When server rendering a HTML file where a client side application intends to re-use the DOM, these fragments must also be written in a way that the original fragment definition, and internal document representation, can be re-created by the client.

Fragment identification

The Longform fragment identifier is used by the parent application to select from the available fragments after parsing a Longform document, or used in a URI fragment to reference the target Longform fragment over HTTP. Other fragments can also embed fragments in the same document by referencing their Longform fragment identifier, this behaviour is described in detail in the declaring the fragment identifier, aside from the root fragment which has none.

The Root Fragment

The Root Fragment is a special, identifier‑less fragment that represents the top‑level document. At most one root fragment may exist per Longform file. The root fragment is typically used when the author wants the parser to emit a complete HTML (or XML) document rather than just a collection of reusable fragments.

After a root fragment is found in the process of parsing a Longform document, all other fragments which do not have Longform identifiers are ignored when rendering the output markup. The Root Fragment often would have the @doctype or @xml directive prefixing it to add a HTML doctype or XML declaration prefixing the output markup.

If a fragment is selected from a Longform document using a URI with no fragment identifier, the root fragment should be selected.

Example Root Fragment using the @doctype directive

@doctype:: html
html::
  head::
    title:: Example Root Fragment
  body::
    h1:: Example Root Fragment
Result
<!doctype html>
<html>
  <head>
    <title>Example Root Fragment</title>
  </head>
  <body>
    <h1>Example Root Fragment</h1>
  </body>
</html>

Unique fragments

Embedded fragment identifiers begin with a single # hash character followed by a single ASCII letter, optionally followed by many letters, numbers, hyphens and underscores within the ASCII character range.

The fragment identifier cannot have any whitespace or other characters preceding it on its line. Only whitespace can follow the fragment identifier, and apart from line breaks, should be ignored by the Longform parser.

The Longform markup block following the identifier must be declared on a line following the fragment identifier and can only be separated by lines of white space or comments. The fragment's markup block must have a single outermost element declared in the Longform syntax and not as HTML. No whitespace can precede the declaration of the outer element.

Example element with an embedded identifier

#embedded-id
section::
  p:: A fragment that can be referenced by its <a href=#embedded-id>identifier</a>
Result
<section id="embedded-id">
  <p>
    A fragment that can be referenced by its <a href="#embedded-id">identifier</a>
  </p>
</section>

Bare fragments

Bare fragment identifiers begin with two # hash characters followed by a single ASCII letter, optionally followed by many letters, numbers, hyphens and underscores within the ASCII character range.

The fragment identifier cannot have any whitespace or other characters preceding it on its line. Only whitespace can follow the fragment identifier, and apart from line breaks, should be ignored by the Longform parser.

The Longform markup block following the identifier must be declared on a line following the fragment identifier and can only be separated by lines of white space or comments. The fragment's markup block must have a single outermost element declared in the Longform syntax and not as HTML.

##alert-something-went-wrong
dialog[open].error::
  p::
    strong:: Something went wrong!
  form[method=dialog]::
    button:: Close
Result
<dialog data-lf="alert-something-went-wrong" open class="error">
  <strong>Something went wrong!</strong>
  <form method="dialog">
    <button>Close</button>
  </form>
</dialog>

Range fragments

Range fragment identifiers begin with a single # hash character followed by a single ASCII letter, optionally followed by many letters, numbers, hyphens and underscores within the ASCII character range. The range fragment type is identified by an [ opening square brace separated from the identifier by whitespace.

The fragment identifier cannot have any whitespace or other characters preceding it on its line. Only whitespace can follow the fragment identifier and the opening brace, and apart from line breaks, should be ignored by the Longform parser.

The Longform markup block following the identifier must be declared on a line following the fragment identifier and can only be separated by lines of white space or comments. The fragment's markup block must have a single outermost element declared in the Longform syntax and not as HTML.

#head-details [
  title:: The range fragment
  meta::
    [name=description]
    [content=Demonstrating the range fragment]
]
Result
<title data-lf="head-details">A range of fragments</title>
<meta data-lf="head-details" name="description" content="Demonstrating the range fragment" />

Text fragments

Text fragments do not include any elements. Programs using Longform output can use text fragments in locations where elements are not allowed such as HTML attributes. Text fragments are particularly useful where Longform is being used as a master document for all translated copy for a webpage.

#aria-label "
  Create a recipe
"
Result
Create a recipe

Whitespace

Whitespace is meaningful in Longform. Any markup indented two spaces out from an element will be outputted as a child of that element.

Some exceptions for this are when native markup of the output language and text are being processed. Or when in a preformatted block.

Declaring elements

Element tags

A sole element tag can be outputted using the element name followed

div::

Elements may be declared using XML namespaces.

h:table[xmlns:h="http://www.w3.org/TR/html4/"]::
  h:tr::
    h:td:: Apples
    h:td:: Bananas

Element attributes

Element attributes are declared after the tag and are wrapped in square brackets [].

div[data-foo=bar][aria-describedby=#baz]::

Alternatively attributes can follow directly after the element tag with 1 level of indentation.

div::
  [data-foo=bar]
  [aria-describedby=#baz]

If an attribute is declared over multiple lines the content is concatenated into a single value. This behaviour does not apply to the element's id if it is defined using the attribute syntax. Classes declared in the attribute syntax will be concatenated with a space separating them.

Attributes also support being wrapped in single and double quotes.

meta::
  [name=description]
  [content=Lorem ipsum dolor sit amet, consectetur adipiscing elit.]
  [content='Quisque a sem et nisl mollis porttitor et sit amet neque.']
  [content="Maecenas suscipit nulla ac suscipit imperdiet. Quisque"]

Element output ids

The elements output markup id can be declared on the line before the tag at the same indentation level with the hash # symbol pre-fixing the id.

This form of giving an element an id also gives it a meaningful Longform identifier.

#element-id
div::

Alternatively the id can follow the tag name, before the closing semicolons. Again with a hash prefixing it. Unlike the form where the

div#element-id::

And finally the id can be declared using the attribute syntax.

div[id=element-id]::
<!-- or -->
div::
  [id=element-id]

If an element has an id declared for it twice only the first declaration is used.

Element classes

Classes can be defined following the element's tag declaration with a period . prefixing each tag.

div#element-id.class-1.class-2.class-3::

Alternatively classes can be defined using the attribute syntax on the lines following the tag definition.

div#element-id::
  [class=class-1]
  [class=class-2 class-3]

Element text and native markup content.

Elements can have text and native markup following the tag declaration with a space between the text and the double colons of the element definition.

div:: Text content with <em>some</em> native markup.

Alternatively, the text and native markup can follow the tag declaration with one extra indentation level.

div::
  Text content with <em>some</em> native markup.

Chained elements

Elements can be chained to create many elements in a single line with each element separated by a double colon.

menu::
  li::a[href=/section1]:: Section 1
  li::a[href=/section2]:: Section 2
  li::a[href=/section3]:: Section 3

Preformatted blocks

Longform does not assign special meaning to any HTML tags so to retain formatting content can be wrapped in curly braces to create a preformatted block.

Escaped preformatted block
When an element is followed by a single curly brace, its content is HTML escaped and its whitespace is preserved at the children's indent level.
pre::
  code:: {
    div::
      <p>
        This content will preserve its formatting including
        the parent <code>div::</code>
      </p>
  }
Result
<pre><code>
  div::
    <p>
      This content will preserve its formatting including
      the parent <code>div::</code>
    </p>
</code></pre>
Un-escaped preformatted text
When an element is followed by a two curly braces, its formatting and content is kept intact.
script:: {{
  console.log('Hello, World!');
}}

style:: {{
  div {
    color: red;
  }
}}
Result
<script>
  console.log('Hello, World!');
</script><style>
  div {
    color: red;
  }
</style>

Comments

Comments can be written outside of a fragment using -- two hyphens. Within a fragment comments in the output markup language's syntax can be used but they will be written to the output alongside all other text in the native output markup.

Embedding fragments

Fragments can be embedded into other fragments as part of the Longform parsing allowing a full document to be created from many fragments. Fragments are embedded using the syntax #[fragment-id].

Sanitization rules are applied to a fragment when it is parsed by Longform by the time it is referenced for embedding the fragment it is already considered trusted content by the Longform parser.

@doctype:: html
html[lang=en]::
  body:: 
    header::
      menu::
        li::a[href=#section1]:: Section 1
        li::a[href=#section2]:: Section 2
    body::
      #[section1]
      #[section2]

#section1
section::
  ...

#section2
section::
  ...

Templating

Longform templates only allow key-value pairs inputted with the values being strings. The template is intended to be passed over to the client where the template might be rendered using client side state. It is assumed that the client has its own means to perform conditional statements and iterate, and is optimized to do so, so those functionalities are left to the scripting language to keep the templating logic lean.

@template
div::
  h3:: Recipe step #{position}

Templated markup

Arbitrary Longform and HTML strings can be inserted in a template using the double hash variable expansion form. See content sanitization for rules on sanitizing untrusted input using this form.

@template
@allow-elements:: strong em
div::
  ##{markup}

Content sanitization

A Longform block can have sanitization rules applied to its content using the @allow-elements, @allow-attributes, @allow-data-attributes, @allow-all directives. These directives are designed to play well with the SanitizerConfig Web API, but for now a sanitizer library must be shipped with the parser to apply the directive rules on any content.

Sanitizer rules apply when expanding variables in a templating context using the double hash expansion syntax ##{var} and in situations where @patchable is used.

Sanitizer defaults

Longform cannot sanitize raw HTML without having a sanitizer library parsing HTML input and it cannot differentiated between text and HTML markup. If the double hash template expansion is used in a situation where no sanitizer is configured the variable expansion SHOULD be ignored by parser implementations. Parsers MAY support an option to bypass this default document level behaviour.

A document specifying no rules allowing elements, attributes or data attributes also SHOULD ignore all input using the double hash variable expansion. Again, a parser might allow equivalent options to the sanitizer directives to bypass this default behaviour.

Element specific sanitizer rules

A element can have rules applied allowing arbitrary markup to be added to the document. Either @allow-elements or @allow-all must be used to allow any elements to persist.

@template
#embedding-content
section::
  header::
    h2:: #{header}
  @allow-elements:: strong em a[href target]
  @allow-attributes: class
  div::
    ##{markup}

Sanitization rules are inherited by child elements. So the following Longform would produce the same results.

@template
@allow-elements:: strong em a[href target]
@allow-attributes: class
#embedding-content
section::
  header::
    h2:: #{header}
  div::
    ##{markup}

Global settings

Settings can be configured document wide using the @global directive from the top level of the document.

@global::
  @allow-elements:: strong em a[href target]
  @allow-attributes: class
  
@template
#template1
section::
  ##{markup}

@template
#template2
section::
  ##{markup}

HTTP Patch

Servers can indicate support for patching a HTTP resource using the PATCH method with the text/vnd.longform media type. This is done by embedding editing information within elements in the document that the client application can select if it wants to allow editing of the presented document.

The patching behaviour works by replacing the content of the editable element identified by its Longform identifier. The element may be the topmost element of a fragment, or deep with the definition of one. Children of an editable element cannot be editable.

For a fragment or element within the document to be editable via PATCH, the document must set its own URI using the @id directive, and it must used the @patchable directive to indicate PATCH support. Each editable fragment or element in the document must then use the @editable directive alongside the sanitization directives to set the edit rules and have a Longform identifier.

Example patchable document

@id:: https://example.com/document
@patchable

#fragment-with-editable-children
div::
  @editable
  @allowed-elements:: ul ol li h2 h3 h4 p strong em
  #editable-1
  div::
    h2:: I can be replaced via PATCH

@editable
@allowed-elements:: li
#item-list [
  li:: I'm lonely
]

#parent-of-list
ul::
  #[item-list]

Example PATCH request

PATCH https://example.com/document HTTP/1.1
Content-Type: text/vnd.longform

#editable-1 [
  <h2>I am replacing you</h2>
]

#item-list [
  li:: Not lonely any more
  li:: Yay
]

The server handling the PATCH request is responsible for locating the patched fragments / elements, applying the sanitization rules (or rejecting the request), and updating the content.

Security considerations

Longform is typically rendered into a HTML document, or parsed client side and rendered into a DOM environment. It is possible to mix untrusted user defined content with the original Longform markup creating the possibility of content injection attacks. Longform provides sanitization APIs that can be configured with allow lists to only allow intended markup to be rendered at the location the untrusted content is being embedded. Where the markup is in the Longform form, the Longform parser should apply the sanitization rules to the markup. Where HTML markup is used the parser should expose APIs so the sanitization rules can be enforced using the Sanitizer API and / or the Trusted Types API. These APIs work in a DOM environment on modern browsers. For server side sanitization or older browser support the parser would need to integrate with a sanitization library such as DOMPurify. Users of a Longform parser should familiarize themselves with how to configure their parser to sanitize HTML content correctly in any environment the content is parsed.

The Longform sanitizer rules assume the Longform document itself is not comprised of untrusted content. Parsers should consider exposing APIs to allow the same sanitization rules be applied document wide so documents can be constructed from multiple sources before being parsed.

It is important for parser implementers to consider that any user content that is rendered into the HTML page as HTML could use the same HTML ids or data attributes as the ones used by the parser and could be picked up by the client application instead of the intended Longform fragment. So it is important that a parsers takes steps to ensure the data attribute names used are unique, maybe using values generated at the time of render. Parsers are encouraged to export CSS selector strings that client applications can use to rebuild the Longform documents, instead of fixed data-attribute naming conventions. For this reason it is also preferred that data attributes are used over HTML ids where user content might be rendered to the page even if the fragment has an embedded id.

Application developers using Longform in the client should consider the affects external scripts might have on a Longform fragment if it is re-attached to the DOM after state changes. Browser extensions could modify the fragment if it is attached to the DOM so consider performing a deep clone on a pristine in memory copy of the fragment instead of re-using previously attached fragments.

Extending Longform

The Longform media type text/vnd.longform is limited to the semantics defined in this document. Its syntax rules offers more flexibility than required for these semantics so extensions of the media type can be made while still being valid text/vnd.longform.

An example scenario where one might want to extend Longform is for a rich page editor that uses Longform's directives and PATCH support to allow editing of sections of the document demarcated as being editable by Longform's @editable directive rules.

By extending Longform the editor could provide its own custom directives which, among other things, could allow the user to embed external content and reference resources known to the host system. Custom directives could also allow other DSLs, for example Markdown or Graphviz's DOT language, to simplify the authoring experience for users.

@editable
#post-content
div::
  p::
    Refer to
    @example:ref:: ARTICLE-03
      Article 03
    for more information
      
A directive that references an article using an immutable label known to the host system. The reference would still be valid even if the article's URL later changed.
@editable
#graph-3
div::
  @example:dot::
    strict graph { 
      a -- b
      a -- b
      b -- a [color=blue]
    } 
An custom directive being used to define a DOT diagram.

In a system where HTTP's content negotiation features specify the semantics of the content the Longform media type should be extended like so: text/vnd.x-example+longform. Any client receiving the document can know to support the custom directives specified by the new media type if they understand it.

If a client was to request the same resource as text/vnd.longform the origin server may pre-process the custom directives and respond with markup known to text/vnd.longform parsers. Custom directives are valid without being pre-processed out of the document, but a text/vnd.longform parser should ignore non-standard directives as it parses the document.

Directives

Directives can be specified at the root level indent of the document, as the part of a Longform element's attributes, among Longform block content, inline after a Longform element's declaration but not in HTML or text content. It is not valid to use directives for specifying ids or classes unless done using the attribute syntax.

Directives declared within element attributes or inline cannot have arguments and are not suffixed with double colons (::). Directives declared among Longform content are only valid if no characters, excluding whitespace, precede the declaration of the directive.

Custom directives

Every directive has two arguments available to it. The inline arguments which follow the double colons. And block arguments, which are the series of lines that are indented out from the directive's declaration line. How these arguments are used is upto the directive's author.

@example:directive:: Inline args are declared following the directive.
  Block args step out one indent
  level from the directive declaration
  and can span many lines.

  Empty lines will be included in the block
  args supplied by the parser.

Typically complex directives will specify their own DSLs using the inline and block arguments and some may use a Longform parser to implement its DSL. So you are likely to see directives that nest Longform syntax, this is not a requirement of Longform but a stylistic convenance available to directive authors.

The double colons following a directive are optional when no inline arguments are provided but stylistically preferred when block args are used.

@lang:: en
@stat:title:: The article title
@stat:description::
  The article description.

@doctype:: html 
html[lang=@lang]::
  head::
    title:: @stat:title
    meta::
      [name=description]
      [content=@stat:description]
  ...
The static site generator Stat can make meta values available by placing directives at the head of the document. These can be referenced by other parts of the document and other documents processed by the framework.
div.card-list::
  @stat:paginate:: 
    article.card::
      a.expanding-link[href=#{stat:url}]::
        [aria-describedby=#{stat:key}]
      h3[id=#{stat:key}]:: #{stat:title}
      div::
        ##{stat:long-description}
Here Stat uses the @stat:paginate directive to iterates over a set of statically generated pages, creating a card with links to each referenced page. The block args supplied to the directive take a Longform template and does not require the @template directive be used.
nav::ol::
  @stat:current-page
  li::a[href=/page-1]:: Page 1
  @stat:current-page
  li::a[href=/page-2]:: Page 2
Another directive from the Stat framework which is used in templates. The directive can set the <a aria-current=page> attribute when the Longform document supplying the page's content uses the same URL referenced by the a tag the directive prefixes.

Longform only specifies where directives use is valid within the syntax. Longform parsers can choose the interfaces made available to custom directives.

The top level directive namespace is also reserved for specifying directives supported by the text/vnd.longform media type. All custom directives should be named using the @example-namespace:directive-name custom directive format.

Standard directives

@id

Sets the URI of the Longform document. A HTTP GET request to the @id using the Accept header text/vnd.longform, or value set by the @type directive should produce the same document unless it has since been modified.

@id:: https://example.com/blog/article-1
@type

Sets the media type of the Longform document. This can be used where Longform is being extended.

@id:: https://example.com/blog/article-1
@type:: text/vnd.x-example+longform

html[lang=en]::
  body::
    @example:custom-directive:: ...
Result
<!doctype html>
<html lang="en">
  <head>...</head>
  <body>...</body>
</html>
@lang

Sets the language of the document. The value should contain a single BCP 47 Language Tag. This value may be be used in the Accept-Language header when making GET or PATCH requests to the same document, it has no effect on the output of parsers and adds no constraints on the content of the document.

@lang:: en-NZ
@dir

Sets the intended direction of the Longform copy at the document level. This value can be used to set the target HTML document's dir value.

A Longform parser should output this as meta information to be used by the handling software.

@dir:: rtl
@doctype

Inserts a doctype declaration at the beginning of a fragment.

@doctype:: html
html[lang=en]::
  head::
    ...
  body::
    ...
Result
<!doctype html>
<html lang="en">
  <head>...</head>
  <body>...</body>
</html>
@xml

Inserts an XML declaration at the beginning of a fragment.

@xml:: version="1.0" encoding="UTF-8"
html::
  [xmlns=http://www.w3.org/HTML/1998/html4]
  [xmlns:xdc=http://www.xml.com/books]
  body::
    ...
Result
<?xml version="1.0" encoding="UTF-8"?>
<html
  xmlns="http://www.w3.org/HTML/1998/html4"
  xmlns:xdc="http://www.xml.com/books"
>
  <body>
    ...
  </body>
</html>
@template

Marks a fragment as being a client side template. When a fragment is a template the Longform parser skips formatting the fragment and outputs it separately to the processed fragments to be passed through to the client. Client side logic can then pass the template into a special Longform template parser and have the HTML output returned.

@template
#button-text "
  Add new #{entityName}
"
@patchable

Asserts to the client that the document can be patched using a HTTP Patch request and the Content-Type header text/vnd.longform. The @patchable directive should be ignored unless the @id directive is used.

@id:: http://example.com/blog/longform-1
@patchable
@editable

Marks the children of an element as editable in a patch request. The element cannot be within a template and must have a Longform id set on it.

@editable
#edit-me
div::
  This content is editable.
@max-length

Sets the maximum bytes a templated input can have.

@editable
@max-length:: 250
@allow-elements::
  h1 p bold em b i a[href target]
#page-header
hgroup::
  h1::
    This is the page header
@allow-elements

In a template this directive instructs the parser what elements can be rendered when applying non-escaped variable expansion within its scope. If used in a patchable document, client side editors should limit what elements can be edited in the editable element. The directive's rules should also be used to sanitize or reject input when merging edits from a HTTP Patch request into the document on the server.

Attributes can be allowed on specific elements by listing them in square brackets directly after the element in the directive's arguments.

If this or the @allow-all directives are not used all elements should be filtered or rejected when editing or applying variable expansion.

Allowed elements should be declared in a space and line break separated list and can be declared using the inline and block args.

This directive can be applied in a @global directive block to set the default rules for a document. The directive applied closest to a template variable expansion or editable element takes precedence.

@editable
@allow-elements:: a[href target] p strong em
#editable
div::
  Edit me!
@allow-attributes

In a template this directive instructs the parser what attributes can be rendered when applying non-escaped variable expansion within its scope. If used in a patchable document client side editors can limit what attributes can be added to the markup. The directive's rules should be used to sanitize or reject input when merging edits from a HTTP Patch request into the document on the server.

If this or the @allow-all directives are not used all attributes should be filtered or rejected when editing or applying variable expansion unless they have been allowed specifically on an element being rendered.

Allowed attributes should be declared in a space and line break separated list and can be declared using the inline and block args.

This directive can be applied in a @global directive block to set the default rules for a document. The directive applied closest to a template variable expansion or editable element takes precedence.

@editable
@allow-attributes:: id name class
@allow-elements::
  a[href target] p strong em form[action]
  label[for] input button[submit]
#editable
div::
  Edit me...
@allow-data-attributes

In a template this directive instructs the parser to allow rendering of data-attributes when applying non-escaped variable expansion within its scope. If used in a patchable document client side editors can allow data-attributes to be added to the markup.

If this or the @allow-all directives are not used all data-attributes should be filtered or rejected when editing or applying variable expansion.

Allowed data attributes should be declared in a space and line break separated list and can be declared using the inline and block args.

This directive can be applied in a @global directive block to set the default rules for a document. The directive applied closest to a template variable expansion or editable element takes precedence.

@editable
@allow-data-attributes
@allow-attributes:: id name class
@allow-elements::
  a[href target] p strong em form[rel action method enctype]
  label[for] input button[submit]
#editable
div::
  Edit me...
@allow-all

This directive instructs the parser to allow all elements, attributes and data-attributes when performing non-escaped variable expansion or when editing a patchable document.

This directive can be applied in a @global directive block to set the default rules for a document. The directive applied closest to a template variable expansion or editable element takes precedence.

@editable
@allow-all
#editable
body::
  Edit me...
@global

Applies directive rules to an entire Longform document. Directives applied before or within a fragment will typically override globally set rules.

@id:: https://example.com/pages/article-1
@patchable
@global::
  @allowed-elements:: h4 p strong em b i small hr br
@preset
@apply

Creates a preset of directive rules that can be applied at any location within the Longform document but are defined at the global level.

@id:: https://example.com/pages/article-1
@patchable
@preset:: text-only
  @allowed-elements:: h4 p strong em b i small hr br

@editable
@apply:: text-only
#foo-bar
div::
@mount

This directive is reserved for parsers to use as a means to create mount points from a Longform document in templating situations. It is not used by the text/vnd.longform media type.