Sometimes you really need a span
A Python-Markdown extension that enables inline <span>
elements with attribute lists:
[content]{#id .class key="value"}
- Wraps inline content in
<span>
elements with appended attributes - Fully compatible with Python-Markdown base and extended syntax, including:
- Inline links
- Reference links
- Wikilinks
- Supports nested spans, links, and nesting inside links
- Renders correctly HTML elements inside attributes (e.g., for MkDocs Material tooltips)
- No parsing overhead — delegates attribute handling to the
attr_list
extension
$ pip install markdown-span-attr
Enable alongside attr_list
in Python:
import markdown
md = markdown.Markdown(extensions=['attr_list', 'span_attr'])
Or in mkdocs.yml
:
markdown_extensions:
- attr_list
- span_attr
[content]{#id .class key="value"}
Renders as:
<span class="class" id="id" key="value">content</span>
See the official extension for attribute lists syntax.
[horace]: #
[[Horace][horace]{.p} is [daring](#quint1){.cit}]{#s1} # nested reference/inline links
[[audax]{.foreign} [daring]]{#s2} # nested spans and unescaped brackets
[[[Poets]] daring in [[words]]{title="Words"}]{#s3} # wikilinks
Renders as:
<span id="s1"><a class="p" href="#">Horace</a> is <a class="cit" href="#quint1">daring</a></span>
<span id="s2"><span class="foreign">audax</span> [daring]</span>
<span id="s3"><a class="wikilink" href="/Poets/">Poets</a> daring in <a class="wikilink" href="/words/" title="Words">words</a></span>
[transilire lineas impune]{: .q title="Varro, <em>De Lingua Latina</em> IX 5" }
Renders as:
<span class="q" title="Varro, <em>De Lingua Latina</em> IX 5">transilire lineas impune</span>
Many would say that supporting arbitrary <span>
elements in Markdown syntax goes against Markdown philosophy, and perhaps it is true. But sometimes you really need a readable span in Markdown, and decadence is inescapable, anyway. So at least be safe with span_attr
not to break compatibility with other extensions.
The extension uses a greedy-safe regular expression to match [content]{: attr-list}
spans, supporting nesting while avoiding premature matches:
\[([^\[\]]*|(?:[^\[\]]*\[[^\[\]]*\](?!\{)[^\[\]]*)*)\](?=\{\:?[ ]*([^\}\n ][^\n]*)[ ]*\})
What it does:
- Matches the innermost span first to support nesting (no other attribute span is allowed inside).
- Allows only content with none or balanced square brackets.
- Uses a lookahead to ensure a valid
{}
attribute list follows.
It matches only [content]
, while attribute parsing is delegated to attr_list
.
The processor is registered with priority 72
. This is chosen to:
- Run after
wikilinks
(75), so links are parsed before wrapping them in spans. - Run before
attr_list
(8), so<span>
elements are inserted beforeattr_list
decorates them.
This placement ensures the extension is compatible with link handling and HTML escaping.
Inline Processors Priority Table (source)
Priority | Pattern Name | Description |
---|---|---|
190 | backtick |
Code spans |
180 | escape |
Backslash escapes |
175 | footnotes * |
Footnote references |
170 | reference |
Reference-style links |
160 | link |
Inline links |
150 | image_link |
Inline images |
140 | image_reference |
Reference-style images |
130 | short_reference |
Shortcut reference-style links |
125 | short_image_ref |
Shortcut reference-style images |
120 | autolink |
Automatic links |
110 | automail |
Automatic email links |
100 | linebreak |
Hard line breaks |
90 | html |
Inline HTML |
80 | entity |
HTML entities |
75 | wikilinks * |
Wiki-style links |
72 | span_attr * |
(this extension) |
70 | not_strong |
Prevent misparsed emphasis |
60 | em_strong |
* -style emphasis |
50 | em_strong2 |
_ -style emphasis |
8 | attr_list * |
Attribute lists |
7 | abbr * |
Abbreviations |
*: Extension