# How Quarkdown Handles Cross-References Between Sections, Figures, and Tables

> Discover how Quarkdown expertly manages cross-references for sections, figures, and tables. Learn about its AST node parsing, resolution, and hyperlink rendering for seamless document navigation within your project.

- Repository: [Giorgio Garofalo/quarkdown](https://github.com/iamgio/quarkdown)
- Tags: how-to-guide
- Published: 2026-04-29

---

**Quarkdown treats cross-references as a first-class language feature by parsing `.ref {id}` calls into `CrossReference` AST nodes, resolving them against `CrossReferenceableNode` targets via the `CrossReferenceResolverHook`, and rendering them as localized hyperlinks during the final output phase.**

Quarkdown, maintained in the `iamgio/quarkdown` repository, implements cross-referencing as a core compiler feature rather than a post-processing step. The system connects reference calls to target elements—such as headings, figures, and tables—through a type-safe AST resolution pipeline that handles parsing, ID matching, and localized rendering.

## The Cross-Reference Architecture

Cross-references in Quarkdown operate through a three-phase pipeline: parsing, resolution, and rendering. Each phase is implemented in dedicated modules within `quarkdown-core` and `quarkdown-html`.

### Parsing Cross-Reference Nodes

When the lexer encounters a reference call like `.ref {my-id}`, it creates a `CrossReference` node defined in [`quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/quarkdown/reference/CrossReference.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/quarkdown/reference/CrossReference.kt). Simultaneously, target elements that implement the `CrossReferenceableNode` interface—such as `Heading`, `Figure`, and `Table`—are parsed with their respective identification metadata.

The `CrossReferenceableNode` interface, defined in [`quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/quarkdown/reference/CrossReferenceableNode.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/quarkdown/reference/CrossReferenceableNode.kt), establishes the contract for all referable elements:

```kotlin
interface CrossReferenceableNode : Node {
    /** The ID used to reference this node (null ⇒ not referable). */
    val referenceId: String?
}

```

### Resolving References to Definitions

The `CrossReferenceResolverHook`, located in [`quarkdown-core/src/main/kotlin/com/quarkdown/core/context/hooks/reference/CrossReferenceResolverHook.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdown/core/context/hooks/reference/CrossReferenceResolverHook.kt), performs the matching between references and definitions. This hook walks the entire AST, collects all `CrossReference` nodes and all `CrossReferenceableNode` implementations, and matches them using the `findDefinitionPair` method.

If a `CrossReference` has a `referenceId` that matches a definition's `referenceId`, the compiler links them. If no match is found, the reference remains unresolved.

### Rendering Localized Links

During the rendering phase, [`quarkdown-html/src/main/kotlin/com/quarkdown/rendering/html/node/QuarkdownHtmlNodeRenderer.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-html/src/main/kotlin/com/quarkdown/rendering/html/node/QuarkdownHtmlNodeRenderer.kt) receives the linked `CrossReference`. It queries the target definition for its `linkableReferenceId` and generates an HTML anchor element:

```html
<a href="#logo"><span class="cross-reference">Figure 1</span></a>

```

The inner text is localized based on the target type (e.g., "Figure", "Section", "Table") and its tracked location number.

## Cross-Referencing Sections, Figures, and Tables

Different element types implement `CrossReferenceableNode` with specific ID storage mechanisms.

### Sections and Headings

In [`quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/base/block/Heading.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/base/block/Heading.kt), the `Heading` class stores its optional ID in the `customId` property. When `customId` is non-null, the heading becomes referable:

```markdown

## Installation Guide {#install}

```

The value `install` becomes the `referenceId` used for resolution purposes.

### Figures

Figures, including `ImageFigure` defined in [`quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/quarkdown/block/Figure.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/quarkdown/block/Figure.kt), expose `referenceId` directly. Authors can specify IDs using the `{#id}` syntax after image blocks:

```markdown
![Quarkdown logo](icon.svg "The icon") {#logo}

```

If a node's `referenceId` equals the placeholder value `"_"` (`CrossReferenceableNode.PLACEHOLDER_REFERENCE_ID`), it can be numbered without becoming a cross-reference target.

### Tables

Tables implement `CrossReferenceableNode` in [`quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/base/block/Table.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-core/src/main/kotlin/com/quarkdown/core/ast/base/block/Table.kt). The optional `{#id}` after the table definition is stored in the `referenceId` property:

```markdown
| Name | Value |
|------|-------|
| A    | 10    |
{#data-table}

```

## Numbering and Localization

When a node is marked as trackable (`canTrackLocation = true`), the location tracking system assigns hierarchical numbers (section 1.2, figure 2, table 3) and stores them in the node's `location` property. The [`QuarkdownHtmlNodeRenderer.kt`](https://github.com/iamgio/quarkdown/blob/main/QuarkdownHtmlNodeRenderer.kt) uses this data to generate localized labels like "Section 1" or "Figure 2".

The renderer emits markup containing `data-location` and `data-localized-kind` attributes, enabling CSS-based styling of reference numbers.

## Usage Examples

### Referencing a Section

```markdown

## Getting Started {#getting-started}

Read the section .ref {getting-started} for details.

```

Generated HTML:

```html
<h2 id="getting-started">Getting Started</h2>
<p>Read the section <a href="#getting-started"><span class="cross-reference">Section 1</span></a> for details.</p>

```

### Referencing a Figure

```markdown
The logo appears in .ref {logo}.

![Quarkdown logo](icon.svg "Quarkdown icon") {#logo}

```

Generated HTML:

```html
<p>The logo appears in <a href="#logo"><span class="cross-reference">Figure 1</span></a>.</p>

<figure id="logo">
  <img src="icon.svg" alt="Quarkdown logo" title="Quarkdown icon">
  <figcaption>Figure 1 – Quarkdown icon</figcaption>
</figure>

```

### Referencing a Table

```markdown
Data are listed in .ref {data-table}.

| Name | Value |
|------|-------|
| A    | 10    |
{#data-table}

```

Generated HTML:

```html
<p>Data are listed in <a href="#data-table"><span class="cross-reference">Table 1</span></a>.</p>

<table id="data-table">
  <thead><tr><th>Name</th><th>Value</th></tr></thead>
  <tbody>
    <tr><td>A</td><td>10</td></tr>
    <tr><td>B</td><td>20</td></tr>
  </tbody>
  <caption>Table 1 – Data overview</caption>
</table>

```

### Standard Library Convenience

The standard library provides a concise wrapper in [`quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Reference.kt`](https://github.com/iamgio/quarkdown/blob/main/quarkdown-stdlib/src/main/kotlin/com/quarkdown/stdlib/Reference.kt):

```kotlin
fun reference(id: String) = CrossReference(id).wrappedAsValue()

```

This allows users to write `.reference {my-id}` instead of `.ref {my-id}`, though both invoke the same underlying resolution mechanism.

## Summary

- **Cross-reference pipeline**: Quarkdown parses `.ref {id}` into `CrossReference` nodes, matches them against `CrossReferenceableNode` implementations via `CrossReferenceResolverHook`, and renders them as localized HTML links.
- **Referable elements**: Headings (via `customId`), Figures (via `referenceId`), and Tables (via `{#id}` syntax) all implement the `CrossReferenceableNode` interface.
- **ID matching**: The `findDefinitionPair` method in [`CrossReferenceResolverHook.kt`](https://github.com/iamgio/quarkdown/blob/main/CrossReferenceResolverHook.kt) matches reference IDs to target definitions during compilation.
- **Numbering system**: Trackable nodes (`LocationTrackableNode`) receive automatic hierarchical numbering that feeds into localized reference labels like "Figure 1" or "Section 2.1".
- **Syntax options**: Use `.ref {id}` or the stdlib `.reference {id}` to create references, and `{#id}` after headings, images, or tables to define targets.

## Frequently Asked Questions

### How do I create a cross-reference target in Quarkdown?

Attach a `{#id}` suffix to any referable element. For headings, place it after the heading text: `## Title {#my-id}`. For figures and tables, place it after the content block. This ID becomes the `referenceId` used by the `CrossReferenceResolverHook` to link references to targets.

### What types of elements can be cross-referenced?

According to the source code, any element implementing `CrossReferenceableNode` can be targeted. This includes `Heading` (sections), `Figure` (including `ImageFigure`), and `Table`. The interface requires a `referenceId` property that uniquely identifies the element within the document.

### How does Quarkdown handle automatic numbering for cross-references?

When a node has `canTrackLocation = true` (implementing `LocationTrackableNode`), the location tracking hook assigns hierarchical numbers during compilation. The [`QuarkdownHtmlNodeRenderer.kt`](https://github.com/iamgio/quarkdown/blob/main/QuarkdownHtmlNodeRenderer.kt) queries this location data to generate localized labels like "Figure 1" or "Table 2", inserting them into the rendered HTML alongside the hyperlink.

### What is the difference between `.ref` and `.reference` in Quarkdown?

There is no functional difference. `.ref` is the core syntax that creates a `CrossReference` node. `.reference` is a convenience wrapper provided by the standard library in [`Reference.kt`](https://github.com/iamgio/quarkdown/blob/main/Reference.kt) that calls the same underlying constructor. Both are resolved by `CrossReferenceResolverHook` and rendered identically.