Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
4 changes: 2 additions & 2 deletions docs/syntax/frontmatter.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ In the frontmatter block, you can define the following fields:

```yaml
---
navigation_title: This is the navigation title <1>
description: This is a description of the page <2>
navigation_title: This is the navigation title. <1>
description: This is a description of the page. <2>
applies_to: <3>
serverless: all
products: <4>
Expand Down
53 changes: 53 additions & 0 deletions src/Elastic.Documentation.Site/Assets/markdown/footnotes.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* Footnotes styling */

.footnotes h4 {
@apply mt-8 mb-10;
}

.footnotes ol {
/* Extra top margin so the gap after the "Footnotes" heading
is more prominent than the gap between individual items. */
@apply mt-4 list-decimal pl-6;
}

.footnotes li {
/* Tighter spacing between items than between heading and list. */
@apply text-ink-light mt-2 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;
display: inline-block;
padding: 0.15em 0.2em;
margin: -0.15em 0;
border-radius: 0.15em;
}

.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 no-underline;
font-family: monospace;
text-decoration: none !important;
vertical-align: super;
font-size: x-small;
}

/* 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
23 changes: 22 additions & 1 deletion src/Elastic.Markdown/IO/MarkdownFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,27 @@ 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 InsertFootnotesHeading(html);
}

private static string InsertFootnotesHeading(string html)
{
const string footnotesContainer = "<div class=\"footnotes\">";

var containerIndex = html.IndexOf(footnotesContainer, StringComparison.Ordinal);
if (containerIndex < 0)
return html;

var hrIndex = html.IndexOf("<hr", containerIndex, StringComparison.Ordinal);
if (hrIndex < 0)
return html;

var endOfHr = html.IndexOf('>', hrIndex);
if (endOfHr < 0)
return html;

return html.Insert(endOfHr + 1, "\n<h4>Footnotes</h4>");
}
}
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
10 changes: 6 additions & 4 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,10 +167,10 @@ public static MarkdownPipeline Pipeline
.UseYamlFrontMatter()
.UseGridTables()
.UsePipeTables()
.UseDirectives()
.UseDefinitionLists()
.UseEnhancedCodeBlocks()
.UseHtmxLinkInlineRenderer()
.UseDirectives()
.UseDefinitionLists()
.UseEnhancedCodeBlocks()
.UseHtmxLinkInlineRenderer()
.DisableHtml()
.UseSpaceNormalizer()
.UseHardBreaks();
Expand Down
Loading
Loading