la.labels

This extension assigns counter-based (or fixed) labels to headings, lists, figures and tables, and lets you use those labels in links to labelled elements.

You can specify where and how labels should appear by:

  1. Adding the -label=... directive to headings, lists, figures and tables; or
  2. Specifying the labels configuration option.

In both cases, you provide a label template.

Note

Remember that, while the following examples all use the -label directive, you can just as easily write templates using the labels configuration option. In the latter case, you may not need to modify the .md file at all in order to produce the labelling systems you want.

1. Label Templates

A label template has several parts.

1.1. Counter Types

A counter type is a system for counting integers. Given -label="1", the 1 is a counter type representing base-10 Arabic numerals. You can also provide A or a for upper/lower-case alphabetic numbering, I or i for upper/lower-case Roman numerals, or one of various longer-form names corresponding to CSS counter styles (e.g., simp-chinese-informal, lower-greek, lower-hexadecimal, etc.).

## Heading {-label="i "}
## Heading

{-label="simp-chinese-formal "}
1. List item
2. List item

Design Notes

The available counter types are essentially the same as the values supported by the list-style-type CSS property, except for:

  • Bullet-points like disc, circle, square, disclosure-open and disclosure-closed (which would simply duplicate literal Unicode characters);
  • Global CSS values like inherit, initial, etc.;
  • The value none;
  • Custom counter styles.

The use of CSS belies some internal complexity. Lists are numbered by generating CSS code that itself produces the labels (because this seems the conventional approach to HTML/CSS lists). However, headings (and figures and tables) are labelled at compile time, the labels becoming part of the HTML document content.

This is partly because the toc extension will read heading text (at compile time, of course), and reproduce it inside the table-of-contents, and we’d ideally like the table-of-contents to contain the same heading labels that the headings themselves do.

To support compile-time labelling, la.labels must implement much of the same counter logic as browsers do to support CSS’s @counter-style construct.

A future version of Lamarkdown may:

  • Provide a configuration option to determine which implementation approach to use for which type of element; and/or
  • Provide a way to define custom counter types, similar to @counter-style.

1.2. Literals

Literal strings can appear as the prefix and suffix of a label. In -label="(1) ", the ( is a prefix, and ) is a suffix. Literals may contain any number of characters, including zero: e.g.:

{-label="@@@ A @@@"}
1. List item
2. List item
3. List item

Literals can consist of:

  • Spaces and all printable symbols other than ASCII digits, letters, quotation marks, ,, - and *.

  • -, when not between alphanumeric characters (where it is considered part of a counter type).

  • *, when not the final character following , (where it has a special meaning, explained below).

  • Characters within an additional pair of quotation marks. There will already be a pair of quotation marks around the entire template (whether -label='...' or label="..."), so the inner quotation marks must be different. For instance:

    {-label="'Table' 1."}
    Column A | Column B
    -------- | --------
    One      | Two
    Three    | Four
    
    Column C | Column D
    -------- | --------
    Five     | Six
    Seven    | Eight
    
  • Quotation marks, when doubled-up within other quotation marks; e.g., -label="''''1''''" produces a base-10 arabic number inside a single set of quotation marks: '1', '2', etc.

    Note: the attribute syntax (the general form attr="value") has its own separate way of escaping characters with \, but this is invisible to the template syntax, so cannot be used to designate literal characters.

A label may consist of just a literal string, with no counter type, which can be useful in unordered lists:

{-label="{-⦿-} "}
* List item
* List item
* List item

1.3. Hierarchical Labels

A parent indicator (if present) is used to make hierarchical labels. It acts as a placeholder for a higher-level label, which then forms part of the lower-level label, preceeding the lower-level counter. (The higher-level label’s own prefix and suffix are removed.)

A template with a parent indicator has a form similar to -label="(L.1) ", where L is the parent indicator, and . is a separator literal (which can be any valid literal). L specifically refers to the label of the next higher list, if one exists.

For instance:

{-label="L.1. "}
1. Item
2. Item

    {-label="[L.A] "}
    1. Item
    2. Item

        {-label="_L_i_ "}
        1. Item
        2. Item

    3. Item

3. Item

In the first label template above, the L. part will be ignored, because there is no higher list level.

Similarly, the parent indicator H refers to the label of the next higher heading (if one exists):

{-label="H.1. "}
## Heading
## Heading

{-label="[H.A] "}
### Heading
### Heading

{-label="_H_i_ "}
#### Heading
#### Heading

### Heading

## Heading

Meanwhile, X refers to the next label label of any kind. For example, here are some <h3> headings, within an <ol> list, underneath another <h2> heading, where the labels are arranged hierarchically:

{-label="1. "}
## Heading
## Heading

{-label="[X.A] "}
1. List item
2. List item

    {-label="_X_i_ "}
    ### Heading
    ### Heading

1.4. Inner Templates

An inner template is the part of a template after the first (non-quoted) ,. A full label template consists of a ,-separated sequence of components (each of which is as described above), optionally ending in ,*.

Inner label templates let you set up several levels of labelling in one place. They apply to nested elements of the same type. For instance, -label="1. ,(a) ,(i) " assigns labels 1., 2., etc. to the current level, (a), (b) to singly-nested elements, and (i), (ii) to doubly-nested elements (all of the same type).

{-label="1. ,(a), (i) "}
1. List item
    1. List item
        1. List item
        2. List item
    2. List item
2. List item
{-label="♠ ,♥, ♣, ♦ "}
* List item
    * List item
        * List item
        * List item
    * List item
* List item

When ,* occurs after the final template component, it applies that template to all more-deeply nested elements indefinitely (until otherwise overridden).

Together with parent indicators, this lets you produce a typical multilevel decimal numbering system with little overhead:

{-label="H.1. ,*"}
## Heading
### Heading
#### Heading
#### Heading
### Heading
## Heading

2. Cross References

Once elements are labelled, you can create links to them (or parts of them) that include those labels in the link text. Specifically, you must:

  • Assign an ID to the target of a link, e.g., by writing {#myid} before or after it. The ID does not need to be on precisely the element that actually has the label; it may also be on any element inside it.
  • Create a link to that ID, where the link text contains ##.

For instance:

Some references to headings [##](#secX) and
[##](#secY), and to [list item ##](#itemU) and
[list item ##](#itemV).

{-label="1. "}
## Heading {#secX}
## Heading {#secY}

{-label="(a) "}
1. List item
    {#itemU}
2. List item
    {#itemV}

You can be more specific about what kind of label you want to refer to, by appending a parent indicator to the ##; e.g., ##H or ##L. You can also have multiple such placeholders for a single link.

A reference to [list item ##L in section ##H](#item).

{-label="1. "}
## Heading
## Heading

{-label="(a) "}
1. List item
2. List item
    {#item}

Design Notes

The use of ## has the advantage that:

  • It is concise, without being too likely to require routine escaping (as a single # may);
  • It appears to reflect a number;
  • It also seems visually-related to the element IDs syntax (#myid), while not being exactly the same; and
  • Since it only applies inside link text, it does not conflict with heading syntax.

One might compare the approach to LaTeX, which uses \label and \ref:

A reference to Section \ref{xyz}.

\section{First}
...

\section{Second}
\label{xyz}
...

Markdown and HTML have element IDs in place of \label. They do not have an exact equivalent of \ref (which just grabs a label). But rather than trying to recreate \ref exactly, we accept the preeminence of hyperlinks in Markdown and HTML, and work within them.

One theoretical weakness of the la.labels approach is that you cannot make a label reference outside of a hyperlink. There seems little practical drawback to this, though.

la.labels does not replace or directly alter the Markdown link-parsing logic, but rather looks for the text inside <a> (anchor) elements once they’ve already been parsed.

3. Suppressing Labels: -no-label

The -no-label directive will suppress a label in places where one would otherwise occur. The counter will be paused, and carry on at the next applicable element.

## Heading {-label="(i) "}
## Heading {-no-label}
## Heading

{-label="(i) "}
1. List item
2. List item
    {-no-label}
3. List item

4. Changing Labels

The label format may be changed part-way through a sequence of elements, simply by assigning a new -label template to one of the elements. For instance:

## Heading {-label="(i) "}
## Heading
## Heading {-label="[A] "}
## Heading

{-label="(i) "}
1. List item
2. List item
3. List item
    {-label="[A] "}
4. List item

5. Options

The labels option is the one key option for influencing document-wide labelling conventions. The remainder of the options below concern the technical implementation details.

Option Description
css_fn

A function taking a single string parameter, which the extension will call to deliver CSS code, under the assumption that it will be applied to the output document. The extension needs this capability in order to implement CSS-based labels, particularly for ordered/unordered lists.

By default, and if it’s available, this will be lamarkdown.css().

If css_fn is None, or if it’s left unspecified and lamarkdown.css() is not available (because the extension is being run externally), then CSS-based labels will be disabled. In this case, the extension will fall back to hard-coding list labels in the HTML document. This may be visually indistinguishable, though not necessarily semantically equivalent.

css_rendering A set containing element types that will receive CSS-based label rendering, if it’s available, rather than hard-coded labels. It’s not currently recommended to change this, because the underlying implementation only currently supports CSS-based rendering for list labels.
directives An object for retrieving directives from HTML tree elements. This should be an instance of lamarkdown.lib.directives.Directives, and the extension will reuse Lamarkdown’s “current” instance by default, if available.
label_processors A list of LabelProcessor objects responsible for orchestrating the labelling of different kinds of HTML elements. It’s not currently recommended to change this.
labels

A dictionary specifying label templates to use in the absence of -label directives.

The dictionary keys are h (for headings), “hn” (for level-n headings specifically), ol (for ordered lists), ul (for unordered lists), figure and table.

The dictionary values are label templates. Just as for the -label directive, these templates can also include parent indicators and inner templates. For instance, you can arrange for a multilevel heading labels, beginning at level-2 headings, with labels = {'h2': 'H.1. ,*'}.

By default, labels is {}.

progress An object accepting error, warning and progress messages. This should be an instance of lamarkdown.lib.Progress, and the extension will reuse Lamarkdown’s “current” instance by default, if available. -