This documentation is also published as Markdown for efficient machine reading: the whole site is indexed at /llms.txt, and every page has a clean Markdown copy under /_llms/. These are generated from the same source and cost far fewer tokens to read than this rendered HTML.

Skip to main content Skip to navigation
Reference

Markdown extensions catalog

Every non-CommonMark Markdown feature Pennington adds — tabs, alerts, code annotations, and cross-reference tags — with syntax, arguments, and emitted classes.

The catalog of non-CommonMark Markdown features enabled in Pennington's Markdig pipeline. Markdig's own built-in syntax (tables, footnotes, and so on) is not covered here.

Extension Syntax Controlled by Doc page
Tabbed code Adjacent fences with tabs=true UseTabbedCodeBlocks Tabbed code
Content tabs # [Label](#tab/id) headings ended by --- UseContentTabs Content tabs
Includes [!INCLUDE …] (full form under Includes) Markdown parser (always on) Reuse shared content
Alerts > [!KIND] inside blockquote UseCustomAlerts Alerts
Code annotations Trailing-comment [!code …] directive UseSyntaxHighlighting Code annotations
Cross-reference tags <xref:uid> or href="xref:uid" Resolved in the response stage Cross-references
Shortcodes <?# Name args /?> Registered IShortcode handlers (pre-parse expansion) Shortcodes

Tabs

Renders a run of consecutive fenced code blocks (starting with one that carries tabs=true) as a single tabbed container with role="tablist", role="tab", and panel regions. The first tab is active by default.

Syntax

markdown
```csharp tabs=true title="C#"
// block A
```

```razor title="Razor"
@* block B *@
```

Each fenced block in the consecutive run becomes a tab panel; only the first block requires tabs=true to open the group.

Arguments

Name Type Default Description
tabs true (absent) Applies to the first fence in the group. Marks a fenced block as the start of a tabbed run; consecutive subsequent fences join the same group.
title string (optionally quoted) pretty language name derived from the info string Applies to each fence in the group. Overrides the label shown on the tab button.

Arguments are key=value pairs; quoted values are allowed. See Code-block argument reference for the full grammar.

Emitted CSS classes

Option Default class Role
OuterWrapperCss not-prose Outer <div> wrapper that opts out of prose styling.
ContainerCss tab-container Container wrapping tablist and panels.
TabListCss tab-list role="tablist" row.
TabButtonCss tab-button role="tab" <button> (carries data-state="active"|"inactive").
TabPanelCss tab-panel aria-labelledby-bound panel wrapping the rendered code block.

Classes are configurable via TabbedCodeBlockRenderOptions passed to UseTabbedCodeBlocks.

Minimal example

Markdown source showing a two-fence tabbed group:

markdown
---
title: Authoring a doc page
description: Populate DocSiteFrontMatter, add an alert, and group code samples into tabs.
tags:
  - authoring
  - front-matter
  - markdown
sectionLabel: Guides
order: 20
---
  
# Authoring a doc page
  
## Callouts
  
> [!NOTE]
> Alerts render with a coloured left border and an icon matching the kind.
  
## Tabbed code groups
  
```bash tabs=true title="dotnet CLI"
dotnet add package Pennington
```
  
```powershell title="PowerShell"
Install-Package Pennington
```
  
```xml title="csproj"
<PackageReference Include="Pennington" Version="*" />
```

Content tabs

Groups a run of DocFX-style tab headings into a single tabset whose panels hold arbitrary Markdown — prose, lists, code, callouts. Distinct from tabbed code: the tab strip sits in the reading flow rather than inside code chrome.

Syntax

A tab opens with a level-1 heading whose only inline is a link to #tab/<id>; the link text is the button label. Consecutive tab headings form one group, ended by a thematic break (---).

markdown
# [Bash](#tab/bash)

Use the bash variant.

# [PowerShell](#tab/pwsh)

Use the PowerShell variant.

---

Dependent tabs

A third path segment — #tab/<id>/<condition> — gates the tab on another group's selection. <condition> is a tab id that has its own plain group elsewhere on the page; the dependent panel shows only when its <id> is the active button and its <condition> is that other group's selected id.

markdown
# [.NET](#tab/lang/linux)

.NET on Linux.

# [.NET](#tab/lang/windows)

.NET on Windows.

---

Arguments

Name Type Default Description
id identifier First segment after #tab/. Identifies the tab; ids are page-wide, so equal ids select together.
condition identifier (absent) Optional second segment. Gates the tab on the selected id of the condition's own group.

Emitted CSS classes

Element Class Attributes Role
Container ctabs data-content-tabs Tabset wrapper; not not-prose.
Tab strip ctabs-bar not-prose role="tablist" Button row; not-prose isolates the buttons from page typography.
Button ctab-btn data-tab, data-active, role="tab", aria-selected One per distinct id.
Panel ctab-panel data-tab, data-condition, data-active, role="tabpanel" One per tab heading; not not-prose, so content keeps prose styling.

The first panel is active in the server-rendered HTML; the client script recomputes selection on load, syncs equal ids page-wide, and persists each choice in localStorage.

Minimal example

A two-tab group whose panels hold prose; the --- thematic break closes the set:

markdown
# [Bash](#tab/bash)

Run the bash script.

# [PowerShell](#tab/pwsh)

Run the PowerShell script.

---

Includes

Splices a referenced Markdown file into the host page during parsing. The directive works as a standalone block or inline within a sentence; the referenced file is expanded recursively.

Syntax

markdown
[!INCLUDE [block partial](../_includes/partial.md)]

Text before [!INCLUDE [inline partial](../_includes/snippet.md)] and after.

Arguments

Name Type Default Description
title string Bracketed label. Parsed but not emitted; present for DocFX compatibility.
path path File path resolved relative to the referencing file. Absolute URLs are not fetched.

Behavior

Case Result
Target file found Content is spliced in; a leading YAML front-matter block is stripped first.
Directive in a fenced code block Left verbatim, so the syntax can be documented.
Target missing Replaced with <!-- Pennington: include not found: <path> -->.
Include cycle, or depth past 16 Replaced with <!-- Pennington: include cycle broken: <path> -->.
Absolute URL path Replaced with <!-- Pennington: include skipped (not a local file): <path> -->.

Relative links and images inside an included file are not rebased — they resolve as if written in the host page.

Minimal example

A standalone block include splicing a shared partial into the host page:

markdown
[!INCLUDE [install steps](../_includes/install.md)]

Alerts

Parses a GitHub-flavored > [!KIND] token as the first line of a blockquote and emits an AlertBlock with two CSS classes. The blockquote form is the only accepted syntax.

Syntax

markdown
> [!NOTE]
> Body text of the alert, CommonMark rendered.

KIND is a case-insensitive alphabetic token.

Arguments

Name Type Default Description
KIND identifier One of NOTE, TIP, CAUTION, WARNING, IMPORTANT. Case-insensitive. Unrecognized tokens still parse, emitting markdown-alert-<kind> using the lowercased value.

Built-in kinds and emitted CSS classes

Every alert receives two classes: markdown-alert (constant) and markdown-alert-<kind>.

Kind Secondary class Typical use
NOTE markdown-alert-note Supplementary information.
TIP markdown-alert-tip Helpful aside.
CAUTION markdown-alert-caution Risky operation.
WARNING markdown-alert-warning Something likely to go wrong.
IMPORTANT markdown-alert-important Must-read information.

Minimal example

Markdown excerpt showing a [!NOTE] block in context:

markdown
---
title: Authoring a doc page
description: Populate DocSiteFrontMatter, add an alert, and group code samples into tabs.
tags:
  - authoring
  - front-matter
  - markdown
sectionLabel: Guides
order: 20
---
  
# Authoring a doc page
  
## Callouts
  
> [!NOTE]
> Alerts render with a coloured left border and an icon matching the kind.
> Supported kinds include `NOTE`, `TIP`, `IMPORTANT`, `WARNING`, and
> `CAUTION`.

Code annotations

After syntax highlighting, each rendered line is scanned for a [!code …] directive inside a language-appropriate comment. The directive is stripped and a CSS class is applied to the line (and optionally to the enclosing <pre>). The word: variant wraps a matching substring; the include-start/include-end/exclude-start/exclude-end directives remove surrounding lines from the output.

Syntax

markdown
```csharp
var x = 1; // [!code highlight]
var y = 2; // [!code ++]
var z = 3; // [!code word:z|renamed from q]
```

The directive must appear inside a comment marker recognized for the language; the marker and any now-empty comment wrapper are removed, leaving trailing content intact. Code-block argument reference is the canonical reference for the full directive set (highlight, ++/--, focus, error, warning, word:, the include/exclude region markers) and the recognized comment markers.

Emitted CSS classes

Line-level classes (highlight, diff-add, diff-remove, focused / blurred, error, warning) are added to the <span class="line"> wrapper. Block-level classes (has-highlighted, has-diff, has-focused, has-errors, has-warnings, has-word-highlights) are added to the outer <pre>. The word: notation emits word-highlight or word-highlight-with-message on the wrapped span, plus the callout elements word-highlight-wrapper, word-highlight-message, word-highlight-arrow-container, word-highlight-arrow-outer, and word-highlight-arrow-inner.

Minimal example

An annotated fence exercising [!code highlight], [!code ++], and [!code --]; the enclosing <pre> receives has-highlighted and has-diff, and the trailing directive comments are stripped from the emitted HTML:

markdown
```csharp
var message = "hello";   // [!code highlight]
var added = "added";     // [!code ++]
var removed = "gone";    // [!code --]
```

Cross-reference tags

xref: links resolve after rendering against the uid-to-route map built from every page's front-matter uid:. Two surface forms are supported: the tag form <xref:uid> and the attribute form [text](xref:uid). Unknown uids emit a diagnostic that surfaces in the dev overlay and in the static-build report.

Syntax

markdown
See <xref:reference.api.pennington-options>.

See [PenningtonOptions](xref:reference.api.pennington-options).

uid is the exact string declared in a page's front-matter uid: key.

Arguments

Name Type Default Description
uid identifier Exact string declared in a page's front-matter uid: key. The tag form derives link text from the target page's title; the attribute form uses the supplied link text verbatim.

Emitted CSS classes

The rewriter emits a standard <a href="…"> element with no added class; styling is delegated to the surrounding prose stylesheet.

Minimal example

Both surface forms resolving the same uid — the tag form derives its link text from the target page title, the attribute form uses the supplied label verbatim:

markdown
See <xref:reference.api.pennington-options> for the full options catalog.

Configure MonorailCSS through [the options record](xref:reference.api.monorail-css-options).

Shortcodes

Shortcodes run before Markdig parses the page rather than as a Markdig extension: each <?# Name args /?> directive is expanded to text or HTML first by a registered IShortcode handler, and the result flows through the pipeline as ordinary markdown. Names are case-insensitive.

Syntax

A call has a self-closing form and a block form with inline content:

markdown
<?# Name positional key="value" /?>

<?# Name ?>inline content<?#/ Name ?>

To document a directive without expanding it, prefix the opener with a backslash — the expander emits the directive verbatim. (Every literal directive on this page uses that escape.)

Built-ins

Name Arguments Description
Version format=full\|major\|minor\|informational (default full) Emits the host application's assembly version.
PackageVersion none Emits Pennington's own published NuGet version.

Unknown names and handler failures degrade to an HTML comment plus a warning diagnostic, so one bad call site never fails the render. See the shortcodes how-to for the handler contract and registration.

See also