From 43a3f121e0b23c7f4ca6d316e2eae47dc8f702cb Mon Sep 17 00:00:00 2001 From: XtremeOwnage <5262735+XtremeOwnageDotCom@users.noreply.github.com> Date: Thu, 3 Sep 2026 14:31:40 -0500 Subject: [PATCH] feat(theme): honour Material's hide front matter Adds page-level opt-out for theme chrome via 'hide:' front matter, matching Material for MkDocs. Supports toc, nav and path; a single scalar is accepted alongside a list, and matching is case-insensitive. Wide auto-generated tables were the motivating case: the table-of-contents column squeezes them for no benefit on a page whose headings are a single H1. The page-metadata reference previously described 'hide' as the awesome-pages 'hide: true' folder toggle, which is a separate mechanism read from a directory's .pages file; both are now documented. --- docs-site/docs/reference/page-metadata.md | 26 ++++++++- src/Netdocs.Core/PageRenderer.cs | 23 ++++++++ .../templates/main.html | 6 +- .../Netdocs.Core.Tests/HiddenElementsTests.cs | 55 +++++++++++++++++++ 4 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 tests/Netdocs.Core.Tests/HiddenElementsTests.cs diff --git a/docs-site/docs/reference/page-metadata.md b/docs-site/docs/reference/page-metadata.md index d54cff9..9adc847 100644 --- a/docs-site/docs/reference/page-metadata.md +++ b/docs-site/docs/reference/page-metadata.md @@ -91,7 +91,31 @@ matching entry in your `nav` configuration — a config `nav` title always wins | `icon` | Navigation icon for the page (any bundled icon name, e.g. `material/key`). | | `tags` | List of tags; collected by the [tags plugin](../plugins/tags.md). | | `template` | Render the page with a different theme template. | -| `hide` | Awesome-pages style `hide: true` drops a folder/page from the nav. | +| `hide` | List of theme chrome to drop from the page — `toc`, `nav`, `path`. See below. | + +## Hiding theme chrome + +`hide` takes a list naming the parts of the page furniture to leave out: + +```yaml +--- +title: Support-Group ABAC Teams +hide: + - toc +--- +``` + +| Value | Effect | +|---|---| +| `toc` | Drops the right-hand table-of-contents column, giving the content its full width. Useful for wide tables. | +| `nav` | Drops the left-hand navigation sidebar. | +| `path` | Drops the breadcrumb trail above the page title. | + +A single value may be given without the list (`hide: toc`). Names are matched +case-insensitively, and unrecognised entries are ignored. + +Not to be confused with `hide: true` in a directory's `.pages` file, which drops +that folder from the navigation entirely. !!! note "Directory-wide defaults" Repeating the same front matter on every page gets tedious. The diff --git a/src/Netdocs.Core/PageRenderer.cs b/src/Netdocs.Core/PageRenderer.cs index 9883873..026c78c 100644 --- a/src/Netdocs.Core/PageRenderer.cs +++ b/src/Netdocs.Core/PageRenderer.cs @@ -68,6 +68,7 @@ public static string Render(TemplateEngine engine, SiteContext site, Page page, ["base_url"] = BaseUrl(page.Url), ["is_homepage"] = string.IsNullOrEmpty(page.Url), ["features"] = new HashSet(site.Config.Theme.Features, StringComparer.OrdinalIgnoreCase), + ["hide"] = HiddenElements(page), ["highlight"] = site.Config.Theme.Highlight, ["extra"] = site.Config.Extra, ["stylesheets"] = ResolveHrefs(site.Config.ExtraCss, assets.Stylesheets), @@ -108,6 +109,28 @@ public static string Render(TemplateEngine engine, SiteContext site, Page page, private static string? FrontMatterText(Page page, string key) => page.FrontMatter.TryGetValue(key, out var v) && v is string s && s.Trim().Length > 0 ? s.Trim() : null; + /// Material's hide: front matter — the chrome a page opts out of + /// (toc, nav, path). Accepts a list or a single value. + internal static HashSet HiddenElements(Page page) + { + var hidden = new HashSet(StringComparer.OrdinalIgnoreCase); + if (!page.FrontMatter.TryGetValue("hide", out var value)) return hidden; + + if (value is string single) + { + hidden.Add(single.Trim()); + } + else if (value is IEnumerable items) + { + foreach (var item in items) + { + if (item?.ToString()?.Trim() is { Length: > 0 } name) hidden.Add(name); + } + } + + return hidden; + } + /// /// Social meta tags must carry absolute URLs to be usable by crawlers, so a site-relative path /// is prefixed with site_url. Already-absolute values are passed through untouched. diff --git a/src/Netdocs.Theme.Material/templates/main.html b/src/Netdocs.Theme.Material/templates/main.html index 16de106..71c6721 100644 --- a/src/Netdocs.Theme.Material/templates/main.html +++ b/src/Netdocs.Theme.Material/templates/main.html @@ -111,6 +111,7 @@ {{~ end ~}}
+ {{~ if !(hide | array.contains "nav") ~}}
@@ -124,7 +125,8 @@
- {{~ if !toc_integrate ~}} + {{~ end ~}} + {{~ if !toc_integrate && !(hide | array.contains "toc") ~}}
@@ -134,7 +136,9 @@
{{~ end ~}}
+{{~ if !(hide | array.contains "path") ~}} {{ include "partials/breadcrumbs.html" }} +{{~ end ~}}
{{ include "partials/content-actions.html" }} {{~ if page.meta.is_post && page.meta.post_tags && (array.size page.meta.post_tags) > 0 ~}} diff --git a/tests/Netdocs.Core.Tests/HiddenElementsTests.cs b/tests/Netdocs.Core.Tests/HiddenElementsTests.cs new file mode 100644 index 0000000..4522107 --- /dev/null +++ b/tests/Netdocs.Core.Tests/HiddenElementsTests.cs @@ -0,0 +1,55 @@ +using Netdocs.Abstractions; +using Xunit; + +namespace Netdocs.Core.Tests; + +/// +/// Covers Material's hide: front matter, which lets a page opt out of theme chrome +/// (toc, nav, path). Wide auto-generated tables use it to reclaim the +/// horizontal space the table-of-contents column would otherwise take. +/// +public class HiddenElementsTests +{ + private static Page PageWith(object? hide) + { + var meta = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (hide is not null) meta["hide"] = hide; + + return new Page + { + SourcePath = "x.md", + RelativePath = "x.md", + FrontMatter = meta, + }; + } + + [Fact] + public void NoFrontMatterHidesNothing() + { + Assert.Empty(PageRenderer.HiddenElements(PageWith(null))); + } + + [Fact] + public void ListValuesAreCollected() + { + var hidden = PageRenderer.HiddenElements(PageWith(new List { "toc", "nav" })); + + Assert.Contains("toc", hidden); + Assert.Contains("nav", hidden); + Assert.DoesNotContain("path", hidden); + } + + [Fact] + public void SingleScalarValueIsAccepted() + { + Assert.Contains("toc", PageRenderer.HiddenElements(PageWith("toc"))); + } + + [Fact] + public void MatchingIsCaseInsensitiveAndTrimmed() + { + var hidden = PageRenderer.HiddenElements(PageWith(new List { " TOC " })); + + Assert.Contains("toc", hidden); + } +}