10. Attributes
Attributes are one of the features that sets Asciidoctor apart from other lightweight markup languages. Attributes can activate features (behaviors, styles, integrations, etc) or hold replacement (i.e., variable) content.
In Asciidoctor, attributes are classified as:
10.1. Attribute Restrictions
All attributes have a name and a value (though the value may be implicit).
The attribute name:
-
must be at least one character long,
-
must begin with a word character (A-Z, a-z, 0-9 or _) and
-
must only contain word characters and hyphens.
In other words, the name cannot contain dots or spaces.
Although uppercase characters are permitted in an attribute entry (the place where an attribute is defined), the attribute name is converted to lowercase before being stored.
The attribute name in an attribute reference is also converted to lowercase before the attribute is resolved.
For example, URI, Uri and uRI are all treated as uri.
(See issue #509 for a proposed change to this restriction).
A best practice is to only use lowercase for letters in the name and avoid starting the name with a number.
The attribute value:
-
can be any inline content and
-
can only contain line breaks if an explicit line continuation is used.
Certain attributes have a restricted range of allowable values. See the entries in the Catalog of Document Attributes for details.
10.2. Attribute Assignment Precedence
The attribute assignment precedence, listed from highest to lowest, is as follows:
-
An attribute defined using the API or CLI
-
An attribute defined in the document
-
The default value of the attribute, if applicable
Let’s use the imagesdir attribute to show how precedence works.
The default value for the imagesdir attribute is an empty string.
Therefore, if the imagesdir attribute is not assigned a value (either in the document, API, or CLI), the processor will assign it the default value of empty string.
If the imagesdir attribute is set in the document (meaning assigned a new value, such as images), that value will override the default value.
Finally, if a value is assigned to the imagesdir attribute via the API or CLI, that value will override both the default value and the value assigned in the document.
It’s possible to alter this order of precedence using a modifier, covered in the next section.
10.2.1. Altering the Attribute Assignment Precedence
You can allow the document to reassign an attribute that is defined via the API or CLI by adding the @ precedence modifier to the end of the attribute value (or, since 1.5.7, the end of the attribute name).
Adding this modifier lowers the precedence so that an assignment in the document still wins out.
We sometimes refer to this as “soft setting” the attribute.
This feature can be useful for assigning default values for attribute, but still letting the document control its own fate.
The @ modifier is removed before the assignment is made.
|
Here’s an example that shows how to set the imagesdir from the CLI with a lower precedence:
$ asciidoctor -a imagesdir=images@ doc.adoc
Since 1.5.7, you can place the modifier at the end of the attribute name:
$ asciidoctor -a imagesdir@=images doc.adoc
It’s now possible to override the value of the imagesdir attribute from within the document:
= Document Title
:imagesdir: new/path/to/images
Let’s update the attribute assignment precedence list defined earlier to reflect this additional rule:
-
An attribute passed to the API or CLI
-
An attribute defined in the document
-
An attribute passed to the API or CLI whose value (or, since 1.5.7, name) ends in
@ -
The default value of the attribute, if applicable
Regardless of whether the precedence modifier is applied, an attribute assignment always overrides the default value.
10.3. Using Attributes: Set, Assign, and Reference
Before you can use an attribute in your document, it must be set. (Sometimes referred to as “toggling on” the attribute).
Some attributes are automatically set when Asciidoctor processes a document. You can also set (or override) an attribute for a document by declaring an attribute entry. For example:
:sectnums:
Many attributes can be assigned a value at the same time:
:leveloffset: 3
The value may be empty, a string (of characters) or a number. A string value may include references to other attributes.
Attributes can be unset using the bang symbol (!).
The ! can be placed either before or after the attribute’s name.
For example, both:
:sectnums!:
and
:!sectnums:
mean unset the sectnums attribute.
In this case, it tells Asciidoctor to not number the sections.
To soft unset an attribute from the CLI or API, you can use the following syntax:
!name=@
The leading ! unsets the attribute while the @ lowers the precedence of the assignment.
This assignment is almost always used to unset a default value while still allowing the document to assign a new one.
One such example is sectids, which is enabled by default.
!sectids=@ switches the setting off.
An attribute reference is an inline element composed of the name of the attribute enclosed in curly brackets. For example:
The value of leveloffset is {leveloffset}.
The attribute reference is replaced by the attribute’s value when Asciidoctor processes the document. Referencing an attribute that is not set is considered an error and is handled specially by the processor.
The following sections will show you how to use attributes on your whole document, individual blocks, and inline elements.
10.4. Setting Attributes on a Document
An attribute entry is the primary mechanism for defining a document attribute in an AsciiDoc document. You can think of an attribute entry as a global variable assignment for AsciiDoc. The document attribute it creates becomes available from that point forward in the document. Attribute entries are also frequently used to toggle features.
An attribute entry consists of two parts: an attribute name and an attribute value.
The attribute name comes first.
It must be at the start of the line and must be enclosed in colons (e.g., :name:).
If present, the attribute value is offset from the name part by at least one space (e.g., :name: value).
Be aware that substitutions automatically get applied to the value by default, as described in Substitutions in an attribute entry.
:name: value
The attribute value is optional. A blank value is often used to set (i.e., activate) a boolean attribute (thus making a blank value implicitly true).
:name:
An exclamation point (!) before (or after) the attribute name unsets the attribute.
In this case, the value is ignored.
:!name:
Attribute entries have the following characteristics:
- Attributes entries can:
-
-
be assigned to a document:
-
through the CLI or API
-
in the document’s header
-
in the document’s body
-
-
be unset (turned off) with a leading (or trailing)
!added to the name -
have default values (in the case of a built-in attribute)
-
have alternate values (in the case of a built-in attribute)
-
span multiple, contiguous lines
-
include inline AsciiDoc content
-
- Attribute entries can not:
-
-
override locked attributes assigned from the command line
-
include AsciiDoc block content (such as, bulleted lists or other types of whitespace-dependent markup)
-
Attributes are typically defined in the document header, though they may also be defined in the body of the document. Once set, an attribute (and its value) are available for use for the remainder of the document. Unless locked by the API, attributes may be reassigned at any subsequent point in the document.
10.4.1. Attribute entry use cases
Attributes entries serve three main purposes:
-
Toggle or configure built-in features
-
Set built-in asset locations
-
Content reuse
Setting built-in attributes
Numerous attribute are reserved for special purposes. These built-in attributes can be used to toggle behavior, such as the table of contents, or control the generated output, such as selecting or configuring a converter. Many built-in attributes only take effect when defined in the document header.
For example, to enable the built-in table of contents, you can define (i.e., set) the toc attribute using an attribute entry in the document header as follows:
:toc:
When the value following an attribute is left empty, as it is in the example above, the default value will be assigned (if any).
The default value for toc is auto.
Therefore, the table of contents will be placed in the default location (below the document’s title) when the document is converted.
If you want the table of contents to be placed on the right side of the document, you must assign the attribute a new value.
:toc: right
The right value will override the default value of auto.
The value assigned to an attribute in the document header replaces the intrinsic value (assuming the attribute is not locked).
Setting asset locations
You can also use attributes to set the base path to images (default: empty), icons (default: ./images/icons), and stylesheets (default: ./stylesheets).
:imagesdir: ./images
:iconsdir: ./icons
:stylesdir: ./styles
Content reuse
If you’re familiar with writing in XML, you might recognize a document attribute as a user-defined entity. When you find yourself typing the same text repeatedly, or text that often needs to be updated, consider assigning it to a document attribute and use that instead.
A prime use case for attribute entries is to promote frequently used URLs and links to the top of the document.
:url-fedpkg: https://apps.fedoraproject.org/packages/rubygem-asciidoctor
Now you can refer to this attribute entry anywhere in the document (where attribute substitution is performed) by surrounding its name in curly braces.
Instead of having to type the URL out longhand in the URL macro, as follows:
Did you know there's an https://apps.fedoraproject.org/packages/rubygem-asciidoctor[Asciidoctor package for Fedora]?
We can replace the target side of the URL macro with a reference to our attribute.
Did you know there's an {url-fedpkg}[Asciidoctor package for Fedora]?
To save even more typing, you can store the whole link in an attribute value.
:link-fedpkg: https://apps.fedoraproject.org/packages/rubygem-asciidoctor[Asciidoctor package for Fedora]
Now you insert this link anywhere in the document using an attribute reference.
Did you know there's an {link-fedpkg}?
Note that the link substitution occurs after the attribute reference is resolved. This works thanks to the default order of substitutions on a paragraph. If you want the URL macro to be resolved eagerly at the time the attribute is assigned, you need to enclose it in a pass macro.
:link-fedpkg: pass:m[https://apps.fedoraproject.org/packages/rubygem-asciidoctor[Asciidoctor package for Fedora]]
Now you can use this link in a section title (where the order of substitutions is different). Let’s dive deeper into which substitutions are applied to an attribute entry and how to alter them.
10.4.2. Substitutions in an attribute entry
The AsciiDoc processor automatically applies substitutions from the header substitution group to the value of an attribute entry prior to the assignment (regardless of where the attribute entry is declared in the document). The header substitution group replaces special characters and attribute references. This is the same group that gets applied to metadata lines (author and revision information) in the document header. To learn about how these substitutions work, refer to the Substitutions chapter.
10.4.3. Altering attribute entry substitutions
If you want the value of an attribute entry to be used as is (not subject to substitutions), or you want to alter the substitutions that are applied, you can enclose the value in the inline pass macro (i.e., pass:[]).
The inline pass macro accepts a list of zero or more substitutions in the target slot, which can be used to control which substitutions are applied to the value.
If no substitutions are specified, no substitutions will be applied.
In order for the inline macro to work in this context, it must completely surround the attribute value. If it’s used anywhere else in the value, it is ignored.
Here’s how to prevent substitutions from being applied to the value of an attribute entry:
:cols: pass:[.>2,.>4]
This might be useful if you’re referencing the attribute in a place that depends on the unaltered text, such as the value of the cols attribute on a table.
Here’s how to apply the quotes substitution to the value of an attribute entry:
:app-name: pass:quotes[MyApp^2^]
Internally, the value is stored as MyApp<sup>2</sup>.
You can inspect the value stored in an attribute using this trick:
[subs=attributes+]
----
{app-name}
----
You can also specify the substitution using the single-character alias, q.
:app-name: pass:q[MyApp^2^]
The inline pass macro kind of works like an attribute value preprocessor. If the processor detects that an inline pass macro completely surrounds the attribute value, it:
-
reads the list of substitutions from the target slot of the macro
-
unwraps the value from the macro
-
applies the substitutions to the value
If the macro is absent, the value is processed with the header substitution group.
You can also change the substitutions that are applied to an attribute at the time it is resolved. This is done by manipulating the substitutions applied to the text where it is referenced. For example, here’s how we could get the processor to apply quote substitutions to the value of an attribute:
:app-name: MyApp^2^
[subs="specialchars,attributes,quotes,replacements,macros,post_replacements"]
The application is called {app-name}.
Notice that we’ve swapped the order of the attributes and quotes substitutions.
This strategy is akin to postprocessing the attribute value.
10.4.4. Splitting attribute values over multiple lines
When an attribute value is very long, it’s possible to split it (i.e., soft-wrap) across multiple lines.
Let’s assume we are working with the following attribute entry:
:long-value: If you have a very long line of text that you need to substitute regularly in a document, you may find it easier to split it neatly in the header so it remains readable to the next person reading your docs code.
You can split the value over multiple lines to make it more readable by inserting a space followed by a backslash (i.e., \) at the end of each continuing line.
:long-value: If you have a very long line of text \
that you need to substitute regularly in a document, \
you may find it easier to split it neatly in the header \
so it remains readable to folks reading your docs code.
The backslash and the newline that follows will be removed from the attribute value when the attribute entry is parsed, making this second example effectively the same as the first. The space before the backslash is preserved, so you have to use this technique at a natural break point in the content.
You can force an attribute value to hard wrap by adding a plus surrounded by spaces before the backslash.
:haiku: Write your docs in text, + \
AsciiDoc makes it easy, + \
Now get back to work!
This syntax ensures that the newlines are preserved in the output document as hard line breaks.
10.4.5. Attribute limitations
Attributes let you do a surprising amount of formatting for what is fundamentally a text replacement tool.
It may be tempting to try and extend attributes to be used for complex replaceable markup.
- Supported
-
Basic in-line AsciiDoc markup is permitted in attribute values, such as:
-
attribute references
-
text formatting (usually wrapped in a pass macro)
-
inline macros (usually wrapped in a pass macro)
-
- Unsupported
-
Complex AsciiDoc markup is not permitted in attribute values, such as:
-
lists
-
multiple paragraphs
-
other whitespace-dependent markup types
-
10.5. Setting Attributes on an Element
An attribute list can apply to blocks, inline quotes text, and macros. The attributes and their values contained in the list will take precedence over attribute entries.
[positional_attribute1,positional_attribute2,name_attribute1="value"]
Attribute lists:
-
apply to blocks as well as macros and inline quoted text
-
can contain positional and named attributes
-
take precedence over global attributes
10.5.1. Positional Attribute
Positional attributes are un-named values at the start of the attribute list. The attribute that they are assigned to depends on the type of the element:
-
icon:(1) size -
image:andimage::(1) alt text, (2) width, (3) height -
Delimited blocks: (1) block name (aka style)
-
Other inline quoted text: (1) role
For example, the following two image macros are equivalent.
image::sunset.jpg[Sunset,300,400]
image::sunset.jpg[alt=Sunset,width=300,height=400]
The second macro is just a duplicate of the first macro written out longhand.
10.5.2. Named Attribute
A named attribute consists of a name and a value separated by an = character (e.g., name=value).
If the value contains a space, comma, or quote character, it must be enclosed in double or single quotes (e.g., name="value with space").
In all other cases, the surrounding quotes are optional.
If present, the enclosing quotes are dropped from the parsed value.
To undefine a named attribute, set the value to None (case sensitive).
10.5.3. Attribute List Substitutions
Attribute references are expanded before the block attribute list is processed. Therefore, it’s not necessary to force substitutions to be applied if you simply want to use a document attribute reference in a block attribute.
If the attribute name (for a positional attribute) or value (for a named attribute) is enclosed in single quotes (e.g., title='Processed by https://asciidoctor.org[Asciidoctor]'), normal substitutions are applied to the value at assignment time (with some exceptions).
This is also true for positional attributes, such as the attribution for a quote block.
No special processing is performed, except for attribute reference expansion, if the value is not enclosed in quotes or is enclosed in double quotes.
If the attribute value contains the same quote character being used to enclose the value, escape the quote character in the value by prefixing it with a backslash (e.g., title="\"Dark Horse\" is a song by George Harrison").
10.5.4. Style
The style attribute is the first positional attribute in an attribute list. It specifies a predefined set of characteristics that should apply to a block element or macro.
For example, a paragraph block can be assigned one of the following built-in style attributes:
-
normal (default, so does not need to be set)
-
literal
-
verse
-
quote
-
listing
-
TIP
-
NOTE
-
IMPORTANT
-
WARNING
-
CAUTION
-
abstract
-
partintro
-
comment
-
example
-
sidebar
-
source
10.5.5. Id
The id attribute specifies a unique name for an element. That name can only be used once in a document.
An id has two purposes:
-
to provide an internal link or cross reference anchor for the element
-
to reference a style or script used by the output processor
Block Assignment
In an attribute list, there are two ways to assign an id attribute to a block element.
-
Prefixing the name with a hash (
#). -
Specifying the name with
id=<name>.
[#goals]
* Goal 1
* Goal 2
Let’s say you want to create a blockquote from an open block and assign it an ID and role.
You add quote (the block style) in front of the # (the ID) in the first attribute position, as this example shows:
[quote#roads, Dr. Emmett Brown]
____
Roads? Where we're going, we don't need roads.
____
| The order of ID and role values in the shorthand syntax does not matter. |
If the ID contains a ., you must define it using either a longhand assignment (e.g., id=classname.propertyname) or the anchor shorthand (e.g., [[classname.propertyname]]).
This is necessary since the . character in the shorthand syntax is the delimiter for a role, and thus gets misinterpreted as such.
|
Inline Assignment
The id (#) shorthand can be used on inline quoted text.
[#free_the_world]*free the world*
10.5.6. Role
An element can be assigned numerous roles.
Block Assignment
In an attribute list, there are two ways to assign a role attribute to a block element.
-
Prefixing the name with a dot (
.). -
Specifying the name with
role=<name>.
[.summary]
* Review 1
* Review 2
[role="summary"]
* Review 1
* Review 2
To specify multiple roles using the shorthand syntax, separate them by dots.
[.summary.incremental]
* Review 1
* Review 2
[role="summary,incremental"]
* Review 1
* Review 2
Inline Assignment
The role (.) shorthand can be used on inline quoted text.
[big goal]*free the world*
[.big.goal]*free the world*
The attribute list preceding formatted text can be escaped using a backslash (e.g., \[role]*bold*).
In this case, the text will still be formatted, but the attribute list will be unescaped and output verbatim.
|
To align with other formatted (i.e., quoted) text in AsciiDoc, roles can now be assigned to text enclosed in backticks.
Given:
[.rolename]`monospace text`
the following HTML is produced:
<code class="rolename">monospace text</code>
Using the shorthand notation, an id (i.e., anchor) can also be specified:
[#idname.rolename]`monospace text`
which produces:
<a id="idname"></a><code class="rolename">monospace text</code>
10.5.7. Options
The options attribute is a versatile named attribute that can contain a comma separated list of values.
It can also be defined globally with an attribute entry.
Block Assignment
In an attribute list, there are three ways to assign an options attribute to a block element.
-
Prefixing the value with a percent sign (
%). -
Specifying the value with
opts=<name> -
Specifying the value with
options=<name>.
Consider a table block with the three option values header, footer, and autowidth.
Here’s how the options are assigned to the table using the shorthand notation (%).
[%header%footer%autowidth]
|===
| Cell A | Cell B
|===
Here’s how the options are assigned to the table using options.
[options="header,footer,autowidth"]
|===
| Cell A | Cell B
|===
Let’s consider the options when combined with other attributes.
[horizontal.properties%step]
property 1:: does stuff
property 2:: does different stuff
[horizontal, role="properties", options="step"]
property 1:: does stuff
property 2:: does different stuff
10.6. Assigning Document Attributes Inline
Document attributes can be assigned using the following syntax:
{set:<attrname>[!][:<value>]}
For example:
{set:sourcedir:src/main/java}
is effectively the same as:
:sourcedir: src/main/java
This is important for being able to assign document attributes in places where attribute entry lines are not normally processed, such as in a table cell.
10.6.1. Handle Missing or Undefined Attributes
As a result of a misconfigured document or inadvertent substitution, an attribute reference may point to a non-existent attribute (e.g., {does-not-exist}).
It could be that the attribute reference itself undefines the attribute (e.g., {set:attribute-no-more!}).
You’ll want to think about how you want the processor to handle these situations and configure it accordingly.
AsciiDoc Python simply drops any line that contains a reference to a missing attribute. This “feature” was designed with AsciiDoc Python’s own template language in mind, which is also based on the AsciiDoc syntax. However, this behavior was never really intended for use in regular AsciiDoc documents. The behavior is frustrating for the author, editor, or reader because it’s not immediately obvious when a line goes missing. Discovering the absence of certain line often requires a painstaking read-through of the document or section, if it’s even noticed at all.
Asciidoctor offers two attributes to alleviate this inconvenience: attribute-missing and attribute-undefined.
Missing attribute
The attribute-missing attribute controls how missing (i.e., unresolved) references are handled.
By default, missing references are left intact so the integrity of the document is preserved (skip).
However, that mode doesn’t help the author track down these references.
To help with that task, Asciidoctor can be configured to warn when a missing reference is encountered (warn).
Asciidoctor can also emulate the behavior of AsciiDoc Python (drop-line), or offer something in between (drop).
Here are the four possible values of the attribute-missing attribute:
skip-
leaves the reference intact without issuing a warning (default setting)
drop-
drops the reference, but not the whole line
drop-line-
drops the whole line on which the reference occurs (matches behavior of AsciiDoc Python)
warn-
leaves the reference intact, but also prints a warning about the missing attribute (recommended)
Consider the following line of AsciiDoc:
Hello, {name}!
Here’s how the line is handled in each case, assuming the name attribute is not defined:
skip
|
Hello, {name}! |
drop
|
Hello, ! |
drop-line
|
|
warn
|
|
If you want the processor to fail when the document contains a missing attribute, set the attribute-missing attribute to warn and pass the --failure-level=WARN option to the processor.
$ asciidoctor -a attribute-missing=warn --failure-level=WARN doc.adoc
When using the API, you can consult the logger for the max severity of all messages reported or look for specific messages in the stack.
There are several exceptions when the attribute-missing attribute is not strictly honored.
One of those cases is the include directive.
If a missing attribute is found in the target of an include directive, the processor will issue a warning about the missing attribute and drop the include directive.
This behavior was chosen because showing the unresolved include directive to the reader is messy.
Another case is the block image macro. If a missing attribute is found in the target of an include directive, the processor will issue a warning about the missing attribute, but leave the image macro unresolved so as to show it as alt text.
A missing attribute reference can safely be used in an ifeval clause without any side effects (i.e., drop) since often the purpose of that statement is to determine whether an attribute resolves to a value.
Undefined attribute
The attribute attribute-undefined controls how expressions that undefine an attribute are handled. By default, the line is dropped since the expression is a statement, not content.
This attribute has two possible values:
drop-
substitute the expression with an empty string after processing it
drop-line-
drop the line that contains this expression (default setting; matches behavior of AsciiDoc Python)
The option skip doesn’t make sense here since the statement is not intended to produce content.
Consider the following declaration:
{set:name!}
Depending on whether attribute-undefined is drop or drop-line, either the statement or the line that contains it will be discarded.
It’s reasonable to stick with the compliant behavior, drop-line, in this case.
| We recommend putting any statement that undefines an attribute on a line by itself. |