From 226f9304c98f4b4826c473b0b38c2de6c773c3b9 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Wed, 26 Nov 2025 12:20:12 +0100 Subject: [PATCH 01/16] Add footnotes --- docs/syntax/footnotes.md | 186 ++++++++++++++++ .../Assets/markdown/footnotes.css | 51 +++++ .../Assets/styles.css | 1 + src/Elastic.Markdown/Myst/MarkdownParser.cs | 9 +- .../Inline/FootnotesTests.cs | 198 ++++++++++++++++++ 5 files changed, 441 insertions(+), 4 deletions(-) create mode 100644 docs/syntax/footnotes.md create mode 100644 src/Elastic.Documentation.Site/Assets/markdown/footnotes.css create mode 100644 tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs diff --git a/docs/syntax/footnotes.md b/docs/syntax/footnotes.md new file mode 100644 index 000000000..15dfbe810 --- /dev/null +++ b/docs/syntax/footnotes.md @@ -0,0 +1,186 @@ +# 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. + +## 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. + +[^1]: This is the first footnote. +[^2]: This is the second footnote. +[^my-note]: This footnote uses a named identifier instead of a number. + +::: + +:::{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. +``` + +::: + +:::: + +## 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]. + +[^concept]: This explains an important concept that's referenced multiple times. + +::: + +:::{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. +``` + +::: + +:::: + +## 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]. + +[^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" + ``` + +::: + +:::{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" + ``` +``` + +::: + +:::: + +## Footnote placement + +Footnote definitions can be placed anywhere in your document, though it's common to place them near where they're referenced or at the end of the document. 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]. + +[^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. + +::: + +:::{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. +``` + +::: + +:::: + +## 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. +- 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 links in the footnote itself. + diff --git a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css new file mode 100644 index 000000000..44ea75d61 --- /dev/null +++ b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css @@ -0,0 +1,51 @@ +/* Footnotes styling */ +.footnotes { + @apply mt-12 pt-6 border-t border-grey-10; +} + +.footnotes hr { + @apply hidden; +} + +.footnotes ol { + @apply list-decimal pl-6 space-y-3; +} + +.footnotes li { + @apply text-sm text-ink-light 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; +} + +.footnote-ref sup { + @apply font-sans; +} + +/* 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; +} + +.footnote-back-ref::before { + content: '↩'; +} + +/* Multiple back references */ +.footnote-back-ref + .footnote-back-ref { + @apply ml-1; +} + diff --git a/src/Elastic.Documentation.Site/Assets/styles.css b/src/Elastic.Documentation.Site/Assets/styles.css index fb3bbf8a6..425c988bc 100644 --- a/src/Elastic.Documentation.Site/Assets/styles.css +++ b/src/Elastic.Documentation.Site/Assets/styles.css @@ -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'; diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index 1aab91540..71e93f272 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -165,10 +165,11 @@ public static MarkdownPipeline Pipeline .UseYamlFrontMatter() .UseGridTables() .UsePipeTables() - .UseDirectives() - .UseDefinitionLists() - .UseEnhancedCodeBlocks() - .UseHtmxLinkInlineRenderer() + .UseDirectives() + .UseDefinitionLists() + .UseFootnotes() + .UseEnhancedCodeBlocks() + .UseHtmxLinkInlineRenderer() .DisableHtml() .UseSpaceNormalizer() .UseHardBreaks(); diff --git a/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs b/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs new file mode 100644 index 000000000..f6780570c --- /dev/null +++ b/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs @@ -0,0 +1,198 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using FluentAssertions; +using JetBrains.Annotations; + +namespace Elastic.Markdown.Tests.Inline; + +public class FootnotesBasicTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + Here's a simple footnote[^1] and another[^2]. + + [^1]: This is the first footnote. + [^2]: This is the second footnote. + """) +{ + [Fact] + public void ContainsFootnoteReferences() + { + Html.Should().Contain("footnote-ref"); + Html.Should().Contain("href=\"#fn:1\""); + Html.Should().Contain("href=\"#fn:2\""); + } + + [Fact] + public void ContainsFootnoteContainer() + { + Html.Should().Contain("class=\"footnotes\""); + } + + [Fact] + public void ContainsFootnoteDefinitions() + { + Html.Should().Contain("id=\"fn:1\""); + Html.Should().Contain("id=\"fn:2\""); + Html.Should().Contain("This is the first footnote."); + Html.Should().Contain("This is the second footnote."); + } + + [Fact] + public void ContainsBackReferences() + { + Html.Should().Contain("footnote-back-ref"); + Html.Should().Contain("href=\"#fnref:1\""); + Html.Should().Contain("href=\"#fnref:2\""); + } +} + +public partial class FootnotesMultipleReferencesTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + First reference[^1] and second reference[^1]. + + [^1]: This footnote is referenced twice. + """) +{ + [Fact] + public void ContainsMultipleReferencesToSameFootnote() + { + Html.Should().Contain("href=\"#fn:1\""); + // Should have at least 2 occurrences + var count = MyRegex().Count(Html); + count.Should().BeGreaterThanOrEqualTo(2); + } + + [Fact] + public void ContainsMultipleBackReferences() + { + // Should have references back to both instances + Html.Should().Contain("href=\"#fnref:1\""); + Html.Should().Contain("href=\"#fnref:2\""); + } + + [System.Text.RegularExpressions.GeneratedRegex("href=\"#fn:1\"")] + private static partial System.Text.RegularExpressions.Regex MyRegex(); +} + +public class FootnotesComplexContentTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + Here's a complex footnote[^complex]. + + [^complex]: This footnote has multiple elements. + + It has multiple paragraphs. + + > And even a blockquote. + + - List item 1 + - List item 2 + """) +{ + [Fact] + public void ContainsComplexFootnoteStructure() + { + Html.Should().Contain("href=\"#fn:1\""); + Html.Should().Contain("This footnote has multiple elements."); + Html.Should().Contain("It has multiple paragraphs."); + } + + [Fact] + public void ContainsBlockquoteInFootnote() + { + Html.Should().Contain("blockquote"); + Html.Should().Contain("And even a blockquote."); + } + + [Fact] + public void ContainsListInFootnote() + { + Html.Should().Contain("List item 1"); + Html.Should().Contain("List item 2"); + } +} + +public class FootnotesWithCodeTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + See the code example[^code]. + + [^code]: Example code: + + ```python + def hello(): + print("Hello, world!") + ``` + """) +{ + [Fact] + public void ContainsCodeBlockInFootnote() + { + Html.Should().Contain("Example code:"); + Html.Should().Contain("hello"); + } +} + +public class FootnotesConsecutiveDefinitionsTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + First[^1], second[^2], third[^3]. + + [^1]: First footnote. + [^2]: Second footnote. + [^3]: Third footnote. + """) +{ + [Fact] + public void HandlesConsecutiveFootnoteDefinitions() + { + Html.Should().Contain("First footnote."); + Html.Should().Contain("Second footnote."); + Html.Should().Contain("Third footnote."); + } + + [Fact] + public void AllFootnoteReferencesAreLinked() + { + Html.Should().Contain("href=\"#fn:1\""); + Html.Should().Contain("href=\"#fn:2\""); + Html.Should().Contain("href=\"#fn:3\""); + } +} + +public class FootnotesInListTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + - Item one + - Item two with footnote[^list] + + [^list]: Footnote from list item. + """) +{ + [Fact] + public void FootnoteWorksInListItem() + { + Html.Should().Contain("href=\"#fn:1\""); + Html.Should().Contain("Footnote from list item."); + } +} + +public class FootnotesWithNamedReferencesTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + Named reference[^my-footnote]. + + [^my-footnote]: This uses a named identifier. + """) +{ + [Fact] + public void HandlesNamedFootnoteIdentifiers() + { + Html.Should().Contain("footnote-ref"); + Html.Should().Contain("This uses a named identifier."); + } +} + From f64a88d8077c8179941c077cb8a57c260de40f75 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Wed, 26 Nov 2025 12:23:24 +0100 Subject: [PATCH 02/16] Add footnotes to nav --- docs/_docset.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/_docset.yml b/docs/_docset.yml index bcc68e978..fd757a739 100644 --- a/docs/_docset.yml +++ b/docs/_docset.yml @@ -97,6 +97,7 @@ toc: - file: definition-lists.md - file: example_blocks.md - file: file_inclusion.md + - file: footnotes.md - file: frontmatter.md - file: icons.md - file: images.md From 550ecc51e06ae6a9a20c624bfaeb79a11f28b6fd Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Wed, 26 Nov 2025 14:34:41 +0100 Subject: [PATCH 03/16] Fix footnotes rendering --- docs/syntax/footnotes.md | 43 +++++----- .../Directives/DirectiveMarkdownExtension.cs | 1 + src/Elastic.Markdown/Myst/MarkdownParser.cs | 8 +- .../Inline/FootnotesTests.cs | 78 +++++++++++++++++++ 4 files changed, 104 insertions(+), 26 deletions(-) diff --git a/docs/syntax/footnotes.md b/docs/syntax/footnotes.md index 15dfbe810..5ff305198 100644 --- a/docs/syntax/footnotes.md +++ b/docs/syntax/footnotes.md @@ -12,10 +12,6 @@ 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. - ::: :::{tab-item} Markdown @@ -34,6 +30,10 @@ You can also use named identifiers[^my-note] which can be more descriptive in yo :::: +[^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. @@ -44,8 +44,6 @@ You can reference the same footnote multiple times throughout your document. 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. - ::: :::{tab-item} Markdown @@ -60,6 +58,8 @@ First reference to the concept[^concept]. Some more text here. Second reference :::: +[^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. @@ -70,6 +70,13 @@ Footnotes can contain multiple paragraphs, lists, blockquotes, and code blocks. 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. @@ -87,13 +94,11 @@ This has a complex footnote[^complex]. def example(): return "Hello from footnote" ``` +``` ::: -:::{tab-item} Markdown - -```markdown -This has a complex footnote[^complex]. +:::: [^complex]: This footnote has multiple elements. @@ -112,15 +117,10 @@ This has a complex footnote[^complex]. def example(): return "Hello from footnote" ``` -``` - -::: - -:::: ## Footnote placement -Footnote definitions can be placed anywhere in your document, though it's common to place them near where they're referenced or at the end of the document. The footnote content will always be rendered at the bottom of the page. +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} @@ -128,14 +128,10 @@ Footnote definitions can be placed anywhere in your document, though it's common 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. - ::: :::{tab-item} Markdown @@ -156,6 +152,9 @@ Even more content in between. :::: +[^early]: This footnote is defined right after the reference. +[^late]: This footnote is defined later in the document. + ## Best practices ### Use descriptive identifiers @@ -181,6 +180,6 @@ Footnotes are automatically numbered in order of first reference, regardless of - 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 links in the footnote itself. - +- Multiple references to the same footnote will show multiple back-reference arrows (↩) in the footnote itself. diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveMarkdownExtension.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveMarkdownExtension.cs index a07570d77..dd3a85952 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveMarkdownExtension.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveMarkdownExtension.cs @@ -35,6 +35,7 @@ public void Setup(MarkdownPipelineBuilder pipeline) // Insert the parser before any other parsers _ = pipeline.BlockParsers.InsertBefore(new DirectiveBlockParser()); } + _ = pipeline.BlockParsers.Replace(new DirectiveParagraphParser()); // Plug the inline parser for CustomContainerInline diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index 71e93f272..f4c86fcd2 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -153,6 +153,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) @@ -167,12 +168,11 @@ public static MarkdownPipeline Pipeline .UsePipeTables() .UseDirectives() .UseDefinitionLists() - .UseFootnotes() .UseEnhancedCodeBlocks() .UseHtmxLinkInlineRenderer() - .DisableHtml() - .UseSpaceNormalizer() - .UseHardBreaks(); + .DisableHtml() + .UseSpaceNormalizer() + .UseHardBreaks(); _ = builder.BlockParsers.TryRemove(); field = builder.Build(); return field; diff --git a/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs b/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs index f6780570c..b042d36d3 100644 --- a/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs @@ -196,3 +196,81 @@ public void HandlesNamedFootnoteIdentifiers() } } +public partial class FootnotesInlineCodeNotParsedTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + Real reference[^1]. Inline code example: `[^1]` should not be parsed. + + [^1]: This is the footnote. + """) +{ + [Fact] + public void InlineCodeFootnoteSyntaxNotParsed() + { + // The inline code `[^1]` should render as code, not as a footnote reference + Html.Should().Contain("[^1]"); + } + + [Fact] + public void OnlyOneBackReference() + { + // Should have only ONE back-reference (inline code shouldn't create a reference) + var count = BackRefRegex().Count(Html); + count.Should().Be(1, "Inline code should not be parsed as footnote references"); + } + + [System.Text.RegularExpressions.GeneratedRegex("footnote-back-ref")] + private static partial System.Text.RegularExpressions.Regex BackRefRegex(); +} + +public class FootnotesInsideDirectiveTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + ::::{tab-set} + + :::{tab-item} Output + + Here's a **bold** and a [link](https://example.com) and footnote[^1]. + + ::: + + :::{tab-item} Markdown + + ```markdown + Here's footnote[^1]. + + [^1]: Example definition in code block. + ``` + + ::: + + :::: + + [^1]: Footnote definitions must be at the document level, not inside directives. + """) +{ + [Fact] + public void OtherInlineElementsWorkInsideDirectives() + { + // Do other inline elements work? + Html.Should().Contain("bold"); + Html.Should().Contain("href=\"https://example.com\""); + } + + [Fact] + public void FootnoteReferencesWorkInsideDirectives() + { + // Footnote REFERENCES work inside directives + Html.Should().Contain("footnote-ref"); + Html.Should().Contain("href=\"#fn:1\""); + } + + [Fact] + public void FootnoteDefinitionsAreAtDocumentLevel() + { + // Footnote DEFINITIONS are rendered at the document level + Html.Should().Contain("
"); + Html.Should().Contain("Footnote definitions must be at the document level"); + } +} + From b1f50e396b5806ad819976753d2cc9e8e6e1a800 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Wed, 26 Nov 2025 15:16:50 +0100 Subject: [PATCH 04/16] Edits --- docs/syntax/footnotes.md | 6 ++ src/Elastic.Markdown/IO/MarkdownFile.cs | 4 +- src/Elastic.Markdown/Myst/MarkdownParser.cs | 1 + .../docs-builder/Http/DocumentationWebHost.cs | 10 +-- .../Inline/FootnotesTests.cs | 80 +++++++++++++++++++ 5 files changed, 95 insertions(+), 6 deletions(-) diff --git a/docs/syntax/footnotes.md b/docs/syntax/footnotes.md index 5ff305198..e4578d777 100644 --- a/docs/syntax/footnotes.md +++ b/docs/syntax/footnotes.md @@ -2,6 +2,12 @@ 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} diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index 07f7bf7d0..389de12e6 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -367,6 +367,8 @@ public static string CreateHtml(MarkdownDocument document) var h1 = document.Descendants().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; } } diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index f4c86fcd2..1dcb96920 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -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(); diff --git a/src/tooling/docs-builder/Http/DocumentationWebHost.cs b/src/tooling/docs-builder/Http/DocumentationWebHost.cs index 3c8442fba..ee2180d39 100644 --- a/src/tooling/docs-builder/Http/DocumentationWebHost.cs +++ b/src/tooling/docs-builder/Http/DocumentationWebHost.cs @@ -131,8 +131,8 @@ private void SetUpRoutes() }) .UseRouting(); - _ = _webApplication.MapGet("/", (ReloadableGeneratorState holder, Cancel ctx) => - ServeDocumentationFile(holder, "index", ctx)); + _ = _webApplication.MapGet("/", (HttpContext httpContext, ReloadableGeneratorState holder, Cancel ctx) => + ServeDocumentationFile(httpContext, holder, "index", ctx)); _ = _webApplication.MapGet("/api/", (ReloadableGeneratorState holder, Cancel ctx) => ServeApiFile(holder, "", ctx)); @@ -145,8 +145,8 @@ private void SetUpRoutes() apiV1.MapElasticDocsApiEndpoints(); #endif - _ = _webApplication.MapGet("{**slug}", (string slug, ReloadableGeneratorState holder, Cancel ctx) => - ServeDocumentationFile(holder, slug, ctx)); + _ = _webApplication.MapGet("{**slug}", (HttpContext httpContext, string slug, ReloadableGeneratorState holder, Cancel ctx) => + ServeDocumentationFile(httpContext, holder, slug, ctx)); } private async Task ServeApiFile(ReloadableGeneratorState holder, string slug, Cancel ctx) @@ -165,7 +165,7 @@ private async Task ServeApiFile(ReloadableGeneratorState holder, string return Results.NotFound(); } - private static async Task ServeDocumentationFile(ReloadableGeneratorState holder, string slug, Cancel ctx) + private static async Task ServeDocumentationFile(HttpContext httpContext, ReloadableGeneratorState holder, string slug, Cancel ctx) { if (slug == ".well-known/appspecific/com.chrome.devtools.json") return Results.NotFound(); diff --git a/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs b/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs index b042d36d3..94e195c1a 100644 --- a/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs @@ -223,6 +223,86 @@ public void OnlyOneBackReference() private static partial System.Text.RegularExpressions.Regex BackRefRegex(); } +public partial class FootnotesCodeBlockNotParsedTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + Real reference[^1]. + + ```markdown + Code block with [^1] reference. + ``` + + [^1]: This is the footnote. + """) +{ + [Fact] + public void CodeBlockRendered() + { + Html.Should().Contain("language-markdown"); + } + + [Fact] + public void OnlyOneBackReference() + { + // At document level, code blocks work correctly - only 1 back-reference + var count = BackRefRegex().Count(Html); + count.Should().Be(1, "Code block content should not be parsed as footnote references"); + } + + [System.Text.RegularExpressions.GeneratedRegex("footnote-back-ref")] + private static partial System.Text.RegularExpressions.Regex BackRefRegex(); +} + +public partial class FootnotesCodeBlockInDirectiveTests(ITestOutputHelper output) : InlineTest(output, + // language=markdown + """ + ::::{tab-set} + + :::{tab-item} Output + + Here's a simple footnote[^1] and another one[^2]. + + ::: + + :::{tab-item} Markdown + + ```markdown + Here's a simple footnote[^1] and another one[^2]. + + [^1]: This is the first footnote. + [^2]: This is the second footnote. + ``` + + ::: + + :::: + + [^1]: This is the first footnote. + [^2]: This is the second footnote. + """) +{ + [Fact] + public void CodeBlockRendered() + { + Html.Should().Contain("language-markdown"); + } + + [Fact] + public void CorrectBackReferenceCount() + { + // Should have exactly 2 back-references (one for [^1] and one for [^2]) + // If code block content is being parsed, we'd see 4 back-references + var count = BackRefRegex().Count(Html); + output.WriteLine("=== HTML ==="); + output.WriteLine(Html); + output.WriteLine("=== END ==="); + count.Should().Be(2, $"Expected 2 back-refs (one per footnote), got {count}. Code block content may be parsed incorrectly."); + } + + [System.Text.RegularExpressions.GeneratedRegex("footnote-back-ref")] + private static partial System.Text.RegularExpressions.Regex BackRefRegex(); +} + public class FootnotesInsideDirectiveTests(ITestOutputHelper output) : InlineTest(output, // language=markdown """ From fb4be7326b4ab066693c299bdc660e916608e196 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Wed, 26 Nov 2025 15:26:38 +0100 Subject: [PATCH 05/16] Remove debug HttpContext parameters --- src/tooling/docs-builder/Http/DocumentationWebHost.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tooling/docs-builder/Http/DocumentationWebHost.cs b/src/tooling/docs-builder/Http/DocumentationWebHost.cs index ee2180d39..3c8442fba 100644 --- a/src/tooling/docs-builder/Http/DocumentationWebHost.cs +++ b/src/tooling/docs-builder/Http/DocumentationWebHost.cs @@ -131,8 +131,8 @@ private void SetUpRoutes() }) .UseRouting(); - _ = _webApplication.MapGet("/", (HttpContext httpContext, ReloadableGeneratorState holder, Cancel ctx) => - ServeDocumentationFile(httpContext, holder, "index", ctx)); + _ = _webApplication.MapGet("/", (ReloadableGeneratorState holder, Cancel ctx) => + ServeDocumentationFile(holder, "index", ctx)); _ = _webApplication.MapGet("/api/", (ReloadableGeneratorState holder, Cancel ctx) => ServeApiFile(holder, "", ctx)); @@ -145,8 +145,8 @@ private void SetUpRoutes() apiV1.MapElasticDocsApiEndpoints(); #endif - _ = _webApplication.MapGet("{**slug}", (HttpContext httpContext, string slug, ReloadableGeneratorState holder, Cancel ctx) => - ServeDocumentationFile(httpContext, holder, slug, ctx)); + _ = _webApplication.MapGet("{**slug}", (string slug, ReloadableGeneratorState holder, Cancel ctx) => + ServeDocumentationFile(holder, slug, ctx)); } private async Task ServeApiFile(ReloadableGeneratorState holder, string slug, Cancel ctx) @@ -165,7 +165,7 @@ private async Task ServeApiFile(ReloadableGeneratorState holder, string return Results.NotFound(); } - private static async Task ServeDocumentationFile(HttpContext httpContext, ReloadableGeneratorState holder, string slug, Cancel ctx) + private static async Task ServeDocumentationFile(ReloadableGeneratorState holder, string slug, Cancel ctx) { if (slug == ".well-known/appspecific/com.chrome.devtools.json") return Results.NotFound(); From 8feac16616f84d5df360cb0da550607c17c69050 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Wed, 26 Nov 2025 15:28:44 +0100 Subject: [PATCH 06/16] Remove decoration --- src/Elastic.Documentation.Site/Assets/markdown/footnotes.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css index 44ea75d61..0c0f85194 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css @@ -26,11 +26,12 @@ /* Footnote reference (superscript link in text) */ .footnote-ref { @apply text-blue-elastic hover:text-blue-elastic-100 font-semibold no-underline; - text-decoration: none; + text-decoration: none !important; } .footnote-ref sup { @apply font-sans; + text-decoration: none !important; } /* Back reference (return arrow in footnote) */ From f1f0b4ce10a84bcf34cfd3b2a626f8cea95a161d Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Wed, 26 Nov 2025 15:58:46 +0100 Subject: [PATCH 07/16] Fix CSS --- src/Elastic.Documentation.Site/Assets/markdown/footnotes.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css index 0c0f85194..e2d333eb2 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css @@ -41,10 +41,6 @@ font-family: monospace; } -.footnote-back-ref::before { - content: '↩'; -} - /* Multiple back references */ .footnote-back-ref + .footnote-back-ref { @apply ml-1; From f3e39b7cb2efd8ef50924dda67f78e5b956bbb81 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Wed, 26 Nov 2025 16:01:47 +0100 Subject: [PATCH 08/16] Format CSS --- .../Assets/markdown/footnotes.css | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css index e2d333eb2..19a1864c8 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css @@ -1,6 +1,6 @@ /* Footnotes styling */ .footnotes { - @apply mt-12 pt-6 border-t border-grey-10; + @apply border-grey-10 mt-12 border-t pt-6; } .footnotes hr { @@ -8,11 +8,11 @@ } .footnotes ol { - @apply list-decimal pl-6 space-y-3; + @apply list-decimal space-y-3 pl-6; } .footnotes li { - @apply text-sm text-ink-light leading-relaxed; + @apply text-ink-light text-sm leading-relaxed; } .footnotes li p { @@ -45,4 +45,3 @@ .footnote-back-ref + .footnote-back-ref { @apply ml-1; } - From 07ff94a17ad72e226a19dedf05b5c67443a67c46 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Wed, 26 Nov 2025 17:32:15 +0100 Subject: [PATCH 09/16] Formatting --- src/Elastic.Markdown/IO/MarkdownFile.cs | 3 +-- src/Elastic.Markdown/Myst/MarkdownParser.cs | 14 +++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index 389de12e6..f2f54b0f6 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -368,7 +368,6 @@ public static string CreateHtml(MarkdownDocument document) if (h1 is not null) _ = document.Remove(h1); - var html = document.ToHtml(MarkdownParser.Pipeline); - return html; + return document.ToHtml(MarkdownParser.Pipeline); } } diff --git a/src/Elastic.Markdown/Myst/MarkdownParser.cs b/src/Elastic.Markdown/Myst/MarkdownParser.cs index 1dcb96920..cf121086a 100644 --- a/src/Elastic.Markdown/Myst/MarkdownParser.cs +++ b/src/Elastic.Markdown/Myst/MarkdownParser.cs @@ -167,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(); field = builder.Build(); return field; From 6149d9fa7c3c25bbb43e023ac43e287299ddceac Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Thu, 27 Nov 2025 11:07:36 +0100 Subject: [PATCH 10/16] Improve CSS --- .../Assets/markdown/footnotes.css | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css index 19a1864c8..984d327b5 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css @@ -1,11 +1,4 @@ /* 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; From 6a248f5500cd1b92c2fbc28752e7815a8188d337 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Thu, 27 Nov 2025 11:15:17 +0100 Subject: [PATCH 11/16] Visual tweaks --- docs/syntax/footnotes.md | 1 + .../Assets/markdown/footnotes.css | 6 +++-- src/Elastic.Markdown/IO/MarkdownFile.cs | 22 ++++++++++++++++++- .../Inline/FootnotesTests.cs | 21 ++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/docs/syntax/footnotes.md b/docs/syntax/footnotes.md index e4578d777..3400912ee 100644 --- a/docs/syntax/footnotes.md +++ b/docs/syntax/footnotes.md @@ -188,4 +188,5 @@ Footnotes are automatically numbered in order of first reference, regardless of - 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. +- In the rendered HTML, collected footnotes are preceded by a `Footnotes` heading at the end of the page. - Multiple references to the same footnote will show multiple back-reference arrows (↩) in the footnote itself. diff --git a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css index 984d327b5..f812406ca 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css @@ -29,9 +29,11 @@ /* 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; + @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 */ diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index f2f54b0f6..b11197891 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -368,6 +368,26 @@ public static string CreateHtml(MarkdownDocument document) 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 = "
"; + + var containerIndex = html.IndexOf(footnotesContainer, StringComparison.Ordinal); + if (containerIndex < 0) + return html; + + var hrIndex = html.IndexOf("', hrIndex); + if (endOfHr < 0) + return html; + + return html.Insert(endOfHr + 1, "\n

Footnotes

"); } } diff --git a/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs b/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs index 94e195c1a..6b8f5f641 100644 --- a/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs @@ -46,6 +46,27 @@ public void ContainsBackReferences() Html.Should().Contain("href=\"#fnref:1\""); Html.Should().Contain("href=\"#fnref:2\""); } + + [Fact] + public void RendersFootnotesHeading() + { + Html.Should().Contain("

Footnotes

"); + } + + [Fact] + public void FootnotesHeadingPrecedesFootnoteContainer() + { + var headingIndex = Html.IndexOf("

Footnotes

"); + var footnotesIndex = Html.IndexOf("
"); + + headingIndex.Should().BeGreaterThanOrEqualTo(0); + footnotesIndex.Should().BeGreaterThanOrEqualTo(0); + footnotesIndex.Should().BeLessThan(headingIndex); + + var hrIndex = Html.IndexOf(" Date: Thu, 27 Nov 2025 11:17:51 +0100 Subject: [PATCH 12/16] Fix tests --- tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs b/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs index 6b8f5f641..09f5d30fb 100644 --- a/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/FootnotesTests.cs @@ -56,8 +56,8 @@ public void RendersFootnotesHeading() [Fact] public void FootnotesHeadingPrecedesFootnoteContainer() { - var headingIndex = Html.IndexOf("

Footnotes

"); - var footnotesIndex = Html.IndexOf("
"); + var headingIndex = Html.IndexOf("

Footnotes

", StringComparison.Ordinal); + var footnotesIndex = Html.IndexOf("
", StringComparison.Ordinal); headingIndex.Should().BeGreaterThanOrEqualTo(0); footnotesIndex.Should().BeGreaterThanOrEqualTo(0); From 818ebd4166afa41b89dad8a07aab5878f68d99f1 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Thu, 27 Nov 2025 12:59:00 +0100 Subject: [PATCH 13/16] Margins --- docs/syntax/footnotes.md | 11 +---------- .../Assets/markdown/footnotes.css | 11 +++++++++-- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/docs/syntax/footnotes.md b/docs/syntax/footnotes.md index 3400912ee..d5ffd1da1 100644 --- a/docs/syntax/footnotes.md +++ b/docs/syntax/footnotes.md @@ -180,13 +180,4 @@ Before adding footnotes, consider whether: ### 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. -- In the rendered HTML, collected footnotes are preceded by a `Footnotes` heading at the end of the page. -- Multiple references to the same footnote will show multiple back-reference arrows (↩) in the footnote itself. +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. \ No newline at end of file diff --git a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css index f812406ca..2f8c9e2dd 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css @@ -1,11 +1,18 @@ /* Footnotes styling */ +.footnotes h4 { + @apply mt-8 mb-12; +} + .footnotes ol { - @apply list-decimal space-y-3 pl-6; + /* Extra top margin so the gap after the "Footnotes" heading + is more prominent than the gap between individual items. */ + @apply list-decimal pl-6 mt-4; } .footnotes li { - @apply text-ink-light text-sm leading-relaxed; + /* Tighter spacing between items than between heading and list. */ + @apply text-ink-light text-sm leading-relaxed mt-2; } .footnotes li p { From 15bf4a696ae0c5cd270be773bcc4f46c84977569 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Thu, 27 Nov 2025 13:00:33 +0100 Subject: [PATCH 14/16] Prettify CSS --- .../Assets/markdown/footnotes.css | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css index 2f8c9e2dd..045eca20f 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css @@ -1,18 +1,18 @@ /* Footnotes styling */ .footnotes h4 { - @apply mt-8 mb-12; + @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 list-decimal pl-6 mt-4; + 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 text-sm leading-relaxed mt-2; + @apply text-ink-light mt-2 text-sm leading-relaxed; } .footnotes li p { @@ -38,9 +38,9 @@ .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; + text-decoration: none !important; + vertical-align: super; + font-size: x-small; } /* Multiple back references */ From 6e5663dac036b84c4e6d84cd45589c59d26a6542 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Thu, 27 Nov 2025 13:18:19 +0100 Subject: [PATCH 15/16] Edit multiple example --- docs/syntax/footnotes.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/syntax/footnotes.md b/docs/syntax/footnotes.md index d5ffd1da1..d36e38f66 100644 --- a/docs/syntax/footnotes.md +++ b/docs/syntax/footnotes.md @@ -48,14 +48,22 @@ You can reference the same footnote multiple times throughout your document. :::{tab-item} Output -First reference to the concept[^concept]. Some more text here. Second reference to the same concept[^concept]. +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]. +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. ``` From 80388cf76c0959f3469235f153ef348f2d68846e Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Thu, 27 Nov 2025 17:44:40 +0100 Subject: [PATCH 16/16] Fix docs and clickable area --- docs/syntax/footnotes.md | 26 +++++++++---------- docs/syntax/frontmatter.md | 4 +-- .../Assets/markdown/footnotes.css | 4 +++ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/docs/syntax/footnotes.md b/docs/syntax/footnotes.md index d36e38f66..a070c748a 100644 --- a/docs/syntax/footnotes.md +++ b/docs/syntax/footnotes.md @@ -4,7 +4,7 @@ Footnotes allow you to add notes and references without cluttering the main text ## Plain paragraph test -This is a plain footnote test[^plain]. No directives involved. +This is a plain footnote test.[^plain] No directives involved. [^plain]: This footnote is in a plain paragraph, outside any directive. @@ -14,7 +14,7 @@ This is a plain footnote test[^plain]. No directives involved. :::{tab-item} Output -Here's a simple footnote[^1] and another one[^2]. +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. @@ -23,7 +23,7 @@ You can also use named identifiers[^my-note] which can be more descriptive in yo :::{tab-item} Markdown ```markdown -Here's a simple footnote[^1] and another one[^2]. +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. @@ -48,22 +48,22 @@ You can reference the same footnote multiple times throughout your document. :::{tab-item} Output -First reference to the concept[^concept]. Some more text here. +First reference to the concept.[^concept] Some more text here. ... -Second reference to the same concept[^concept]. +Second reference to the same concept.[^concept] ::: :::{tab-item} Markdown ```markdown -First reference to the concept[^concept]. Some more text here. +First reference to the concept.[^concept] Some more text here. ... -Second reference to the same concept[^concept]. +Second reference to the same concept.[^concept] [^concept]: This explains an important concept that's referenced multiple times. ``` @@ -82,14 +82,14 @@ Footnotes can contain multiple paragraphs, lists, blockquotes, and code blocks. :::{tab-item} Output -This has a complex footnote[^complex]. +This has a complex footnote.[^complex] ::: :::{tab-item} Markdown ```markdown -This has a complex footnote[^complex]. +This has a complex footnote.[^complex] [^complex]: This footnote has multiple elements. @@ -140,9 +140,9 @@ Footnote definitions should be placed at the document level (not inside directiv :::{tab-item} Output -Here's text with a footnote[^early]. +Here's text with a footnote.[^early] -More content here, and another footnote[^late]. +More content here, and another footnote.[^late] Even more content in between. @@ -151,11 +151,11 @@ Even more content in between. :::{tab-item} Markdown ```markdown -Here's text with a footnote[^early]. +Here's text with a footnote.[^early] [^early]: This footnote is defined right after the reference. -More content here, and another footnote[^late]. +More content here, and another footnote.[^late] Even more content in between. diff --git a/docs/syntax/frontmatter.md b/docs/syntax/frontmatter.md index 43da89801..67e8e00d8 100644 --- a/docs/syntax/frontmatter.md +++ b/docs/syntax/frontmatter.md @@ -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> diff --git a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css index 045eca20f..169376fe2 100644 --- a/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css +++ b/src/Elastic.Documentation.Site/Assets/markdown/footnotes.css @@ -27,6 +27,10 @@ .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 {