Skip to content
Merged
1 change: 1 addition & 0 deletions docs/_docset.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
project: 'doc-builder'
max_toc_depth: 2
# indicates this documentation set is not linkable by assembler.
Expand Down Expand Up @@ -97,6 +97,7 @@
- file: definition-lists.md
- file: example_blocks.md
- file: file_inclusion.md
- file: footnotes.md
- file: frontmatter.md
- file: icons.md
- file: images.md
Expand Down
191 changes: 191 additions & 0 deletions docs/syntax/footnotes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
# Footnotes

Footnotes allow you to add notes and references without cluttering the main text. They're automatically numbered and linked, providing an elegant way to include supplementary information, citations, or explanations.

## Plain paragraph test

This is a plain footnote test[^plain]. No directives involved.

[^plain]: This footnote is in a plain paragraph, outside any directive.

## Basic footnotes

::::{tab-set}

:::{tab-item} Output

Here's a simple footnote[^1] and another one[^2].

You can also use named identifiers[^my-note] which can be more descriptive in your source files.

:::

:::{tab-item} Markdown

```markdown
Here's a simple footnote[^1] and another one[^2].

You can also use named identifiers[^my-note] which can be more descriptive in your source files.

[^1]: This is the first footnote.
[^2]: This is the second footnote.
[^my-note]: This footnote uses a named identifier instead of a number.
```

:::

::::

[^1]: This is the first footnote.
[^2]: This is the second footnote.
[^my-note]: This footnote uses a named identifier instead of a number.

## Multiple references

You can reference the same footnote multiple times throughout your document.

::::{tab-set}

:::{tab-item} Output

First reference to the concept[^concept]. Some more text here. Second reference to the same concept[^concept].

:::

:::{tab-item} Markdown

```markdown
First reference to the concept[^concept]. Some more text here. Second reference to the same concept[^concept].

[^concept]: This explains an important concept that's referenced multiple times.
```

:::

::::

[^concept]: This explains an important concept that's referenced multiple times.

## Complex footnotes

Footnotes can contain multiple paragraphs, lists, blockquotes, and code blocks. Subsequent content must be indented to be included in the footnote.

::::{tab-set}

:::{tab-item} Output

This has a complex footnote[^complex].

:::

:::{tab-item} Markdown

```markdown
This has a complex footnote[^complex].

[^complex]: This footnote has multiple elements.

It has multiple paragraphs with detailed explanations.

> This is a blockquote inside the footnote.
> It can span multiple lines.

- List item one
- List item two
- List item three

You can even include code:

```python
def example():
return "Hello from footnote"
```
```

:::

::::

[^complex]: This footnote has multiple elements.

It has multiple paragraphs with detailed explanations.

> This is a blockquote inside the footnote.
> It can span multiple lines.

- List item one
- List item two
- List item three

You can even include code:

```python
def example():
return "Hello from footnote"
```

## Footnote placement

Footnote definitions should be placed at the document level (not inside directives like tab-sets, admonitions, or other containers). Footnote references can be used anywhere in your document, including inside directives. The footnote content will always be rendered at the bottom of the page.

::::{tab-set}

:::{tab-item} Output

Here's text with a footnote[^early].

More content here, and another footnote[^late].

Even more content in between.

:::

:::{tab-item} Markdown

```markdown
Here's text with a footnote[^early].

[^early]: This footnote is defined right after the reference.

More content here, and another footnote[^late].

Even more content in between.

[^late]: This footnote is defined later in the document.
```

:::

::::

[^early]: This footnote is defined right after the reference.
[^late]: This footnote is defined later in the document.

## Best practices

### Use descriptive identifiers

While you can use simple numbers like `[^1]`, descriptive identifiers like `[^api-note]` make your source more maintainable.

### Keep footnotes focused

Each footnote should contain a single, focused piece of information. If you find yourself writing very long footnotes, consider whether that content belongs in the main text.

### Consider alternatives

Before adding footnotes, consider whether:
- The information is important enough to be in the main text.
- A link to external documentation would be more appropriate.
- An admonition (note, warning, etc.) would be clearer.

### Numbering

Footnotes are automatically numbered in order of first reference, regardless of the identifier you use in your source. This means `[^zebra]` appearing before `[^apple]` will be numbered as footnote 1.

## Technical notes

- Footnote identifiers are case-sensitive.
- Identifiers can contain letters, numbers, and hyphens.
- **Footnote definitions must be placed at the document level**, not inside directives (tab-sets, admonitions, etc.). Footnote references can be used anywhere.
- All footnotes are collected and displayed at the end of the page, regardless of where they're defined in the source.
- Multiple references to the same footnote will show multiple back-reference arrows (↩) in the footnote itself.
47 changes: 47 additions & 0 deletions src/Elastic.Documentation.Site/Assets/markdown/footnotes.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* Footnotes styling */
.footnotes {
@apply border-grey-10 mt-12 border-t pt-6;
}

.footnotes hr {
@apply hidden;
}

.footnotes ol {
@apply list-decimal space-y-3 pl-6;
}

.footnotes li {
@apply text-ink-light text-sm leading-relaxed;
}

.footnotes li p {
@apply inline;
}

.footnotes li p:not(:last-child) {
@apply mb-3;
}

/* Footnote reference (superscript link in text) */
.footnote-ref {
@apply text-blue-elastic hover:text-blue-elastic-100 font-semibold no-underline;
text-decoration: none !important;
}

.footnote-ref sup {
@apply font-sans;
text-decoration: none !important;
}

/* Back reference (return arrow in footnote) */
.footnote-back-ref {
@apply text-blue-elastic hover:text-blue-elastic-100 ml-2 no-underline;
text-decoration: none;
font-family: monospace;
}

/* Multiple back references */
.footnote-back-ref + .footnote-back-ref {
@apply ml-1;
}
1 change: 1 addition & 0 deletions src/Elastic.Documentation.Site/Assets/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
@import './markdown/dropdown.css';
@import './markdown/table.css';
@import './markdown/definition-list.css';
@import './markdown/footnotes.css';
@import './markdown/images.css';
@import './markdown/math.css';
@import './markdown/image-carousel.css';
Expand Down
4 changes: 3 additions & 1 deletion src/Elastic.Markdown/IO/MarkdownFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,8 @@ public static string CreateHtml(MarkdownDocument document)
var h1 = document.Descendants<HeadingBlock>().FirstOrDefault(h => h.Level == 1);
if (h1 is not null)
_ = document.Remove(h1);
return document.ToHtml(MarkdownParser.Pipeline);

var html = document.ToHtml(MarkdownParser.Pipeline);
return html;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public void Setup(MarkdownPipelineBuilder pipeline)
// Insert the parser before any other parsers
_ = pipeline.BlockParsers.InsertBefore<ThematicBreakParser>(new DirectiveBlockParser());
}

_ = pipeline.BlockParsers.Replace<ParagraphBlockParser>(new DirectiveParagraphParser());

// Plug the inline parser for CustomContainerInline
Expand Down
16 changes: 9 additions & 7 deletions src/Elastic.Markdown/Myst/MarkdownParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ private static MarkdownPipeline MinimalPipeline
return field;
var builder = new MarkdownPipelineBuilder()
.UseYamlFrontMatter()
.UseFootnotes() // Must match Pipeline to avoid inconsistent footnote handling
.UseInlineAnchors()
.UseHeadingsWithSlugs()
.UseDirectives();
Expand All @@ -153,6 +154,7 @@ public static MarkdownPipeline Pipeline
var builder = new MarkdownPipelineBuilder()
.UseInlineAnchors()
.UsePreciseSourceLocation()
.UseFootnotes() // Must be before UseDiagnosticLinks to ensure FootnoteLinkParser is inserted correctly
.UseDiagnosticLinks()
.UseHeadingsWithSlugs()
.UseEmphasisExtras(EmphasisExtraOptions.Default)
Expand All @@ -165,13 +167,13 @@ public static MarkdownPipeline Pipeline
.UseYamlFrontMatter()
.UseGridTables()
.UsePipeTables()
.UseDirectives()
.UseDefinitionLists()
.UseEnhancedCodeBlocks()
.UseHtmxLinkInlineRenderer()
.DisableHtml()
.UseSpaceNormalizer()
.UseHardBreaks();
.UseDirectives()
.UseDefinitionLists()
.UseEnhancedCodeBlocks()
.UseHtmxLinkInlineRenderer()
.DisableHtml()
.UseSpaceNormalizer()
.UseHardBreaks();
_ = builder.BlockParsers.TryRemove<IndentedCodeBlockParser>();
field = builder.Build();
return field;
Expand Down
Loading
Loading