Jekyll extensions, plug-ins
Jekyll is flexible and extendable, accepts various plugins, liquid or markdown extensions, and we are using a bit of all of them.
generate_links
Our generate_links.rb Jekyll plugin creates the content of the ${PROJECT_ROOT}/_data/links folder. The generated link files are created from all the H1-H6 headings, named anchors, and the page self-links of our markdown pages. The links are used for autolink/tooltip generation and page navigation ordering.
It is gated by the JEKYLL_BUILD_LINKS=yes environment variable and runs on the :site, :post_render hook – i.e. after Jekyll has rendered every page to HTML, so it can extract anchor information from the final DOM.
generate_tooltips
The generate_tooltips.rb Jekyll plugin is responsible for adding autolink/tooltip items to our pages. It is gated by the JEKYLL_BUILD_TOOLTIPS=yes environment variable and registers on two Jekyll hooks within the same build:
:site, :pre_render– loads the link dictionary built bygenerate_linksfrom_data/links/, sorts entries by title length (longest first to avoid partial-word matches), and applies aliases and exclusions[:pages, :documents], :pre_render– iterates each page’s Markdown content, splits it into safe-to-process spans (skipping fenced code blocks, existing links, headings) and injects<a href="...">title</a>HTML anchors directly into the Markdown source. kramdown then preserves these inline<a>tags as-is when converting Markdown to HTML.
It is far-far not optimal, makes the whole rendering process very slow now and needs enhancements badly, please feel free to contribute to make it more effective!
autolink/tooltip
This enhancement allows us to create automatic links and Wikiwand like tooltips (previews of linked articles) in the markdown documentation pages to
- other local pages, based on their title text [h1 heading]
- headings [h2-h6] of any local pages, based on the heading text
- named anchors of any local pages, based on the anchor text
- any of the above, via given text parts that enumerated in the
${PROJECT_ROOT}/_data/link_aliases.ymlfile - any of the above, via given text parts that marked using our custom markdown notation
- any of the above, via our markdown_link liquid include
- external pages, via given text parts enumerated in the
${PROJECT_ROOT}/_data/external_links.ymlfile
The automatic link and tooltip generation primarily use one-to-one text matching of the given text in the above enumerated cases to the markdown content, from the longest sentence to the shortest one. You can exclude specific sentences from the process using ${PROJECT_ROOT}/_data/excluded_titles.yml.
These can all be overridden by using the [[title|id]] markdown extension, which always has priority over these rules (even over the exclusions!).
The basic flow is as follows:
- in the first Jekyll build (
JEKYLL_BUILD_LINKS=yes)- the Jekyll liquid parser creates named anchors from our
markdown_linkliquid includes - kramdown converts each page’s Markdown into HTML
- on
:site, :post_render,generate_linksparses the rendered HTML with Nokogiri and writes one link descriptor per page (id, url, title, priority) to${PROJECT_ROOT}/_data/links/
- the Jekyll liquid parser creates named anchors from our
- in the second Jekyll build (
JEKYLL_BUILD_TOOLTIPS=yes),generate_tooltips- on
:site, :pre_render: loads every_data/links/*.ymlinto a single dictionary, sorted by title length in reverse order, then merges aliases from${PROJECT_ROOT}/_data/link_aliases.ymland removes excluded entries listed in${PROJECT_ROOT}/_data/excluded_titles.yml - on
[:pages, :documents], :pre_render: walks the Markdown source of every page/document, finds plain-text matches against the dictionary and rewrites them into inline<a class="...">title</a>HTML anchors that kramdown will leave intact
- on
- at runtime
- the bundled JS (
_js/custom/navigation.js, packed intoassets/js/main.min.js) selects every anchor that carries the authoring classcontent-tooltipand attaches the hover/click tooltip behaviour to it; the rendered tooltip container then receives one of the runtime state classestext-content-tooltiporfull-content-tooltipdepending on the previewed content size (these two are not authoring classes – do not write them in Markdown)
- the bundled JS (
markdown_link
This pure liquid include file can be used to generate markdown links from the links created by our generate_links plugin with various additional parameters.
It can declare
[ref:{{ md_link_id }}]: {{ md_link_url | relative_url }}
or define
[{{ md_link_title }}]({{ md_link_url | relative_url }}){: {{ md_link_style }} }
a markdown link, where the link url and title is handled automatically using the given ID.
the url and the default title text must not be provided here (though can be overridden if needed), the lookup of them will be automatic based on the given ID, the generate_links collected links in the ${PROJECT_ROOT}/_data/links folder are also available via the site.data.links liquid variable, markdown_link uses this variable as well to search for the given ID and the corresponding title and url.
The examples below show the include invocation (top fence) and the raw Markdown that the include expands to (bottom fence). Jekyll then runs kramdown on that Markdown to produce the final HTML <a> tag, so what you see is an intermediate representation, not the final rendered output.
You can use the following parameters to adjust the composition of the above md_link_ options:
-
decl, a boolean string'yes','no'or valuetrue,falsethat controls if the generated link is a declaration, or definition, e.g.:{% include markdown_link id='id' decl=true %}Expands to:
[ref:id]: urlthat you can use later like
[custom title][ref:id] -
id, the link ID, e.g.:{% include markdown_link id='id' %}Expands to:
[title](url){: class="nav-link" }the
class="nav-link"style is always added to the link, except ifoutOfFrameis set toyes(ornewPageis set toyes, see below). -
title, a custom title that can override the one that belongs to the given link ID, e.g.:{% include markdown_link id='id' title='custom title' %}Expands to:
[custom title](url){: class="nav-link" } -
withTooltip, a boolean string'yes','no'or valuetrue,falsethat controls if the generated link should have css class style that is treated as autolink/tooltip, e.g.:{% include markdown_link id='id' withTooltip=true %}Expands to:
[title](url){: class="nav-link content-tooltip" }generate_tooltips will use the
content-tooltipclass to generate autolink/tooltip. -
newPage, a boolean string'yes','no'or valuetrue,falsethat controls if a navigational click on the generated final HTML anchor element will open the link in a new browser tab, or window, e.g.:{% include markdown_link id='id' newPage=true %}Expands to:
[title](url){: target="_blank" }Note: the current include implementation replaces the class IAL with
target="_blank", soclass="nav-link"is dropped whennewPage=true. The link therefore falls out of the SPA navigation flow and triggers a full page load in the new tab – which is the desired behaviour for new-tab links. -
outOfFrame, a boolean string'yes','no'or valuetrue,falsethat controls if a navigational click on the generated final HTML anchor element will open the linked content in the right frame of the page, or will trigger a full page load that replacing the current content entirely, e.g.:{% include markdown_link id='id' outOfFrame=true %}Expands to:
[title](url){: class="" }The empty
class=""IAL means thenav-linkstyle is omitted, so the link triggers a full page load instead of an SPA content swap.
[[title|id]] markdown extension
The generate_tooltips plugin can interpret this format of markdown linking. It accepts the following formats
-
[[title]]This can force autolink/tooltip generation to use the encapsulatedtitlestring for title matching. This can be useful when the given title is part of a longer title sentence that otherwise will be used for the autolink/tooltip composition.
In the following sentenceThe syslog-ng OSE darwin-oslog() source options explains how you can configure the new native macOS system log source.the darwin-oslog() source options normally will become an autolink/tooltip, as we have a page with this title which its own link is part of the
${PROJECT_ROOT}/_data/linkscollection produced by generate_links, therefore will be transformed automatically to an autolink/tooltip by generate_tooltips.Using
The [[syslog-ng OSE]] darwin-oslog() [[source]] options explains how you can configure the new native macOS system log source.the syslog-ng OSE and the source will become an autolink/tooltip instead.
-
[[title|id]]This can force autolink/tooltip generation to use a differenttitlestring for a given linkid, or a differentid(that way the link belongs to thatid) for a giventitlestring. This can be useful when the given title is ambiguous in a given context, and one would like to force the generated autolink/tooltip to point to a (different) specific location.
In the following sentenceThe syslog-ng OSE darwin-oslog() [[source|adm-src-macos]] is a new native macOS system log source.the first occurrence of
sourcewill become an autolink/tooltip to theadmin-guide/060_Sources/085_macOS/README.mdpage, whilst the second one will point to the defaultadmin-guide/200_About/002_Glossary#source. -
[[title|-]]This is a special case which can be useful in a given context to temporarily disable the generation of an autolink/tooltip, using this form thetitletext will not be touched at all, e.g.This can lead to the [[source|-]] of a misunderstanding.will produce
This can lead to the source of a misunderstanding.
without any autolink/tooltip generated (as in this context pointing to the default
admin-guide/200_About/002_Glossary#sourcepage is meaningless, and confusing). -
Any other combination with a missing
titleoridleads to an error message during build time and keeps the original special markdown part in the HTML output to signal the error visually as well, e.g.This can lead to the [[source|]] markdown reflected back in the final HTML output.will produce
This can lead to the [[source|]] markdown reflected back in the final HTML output.
A small cross-reference table of the use cases
| [[ case ]] | found | success | result |
|---|---|---|---|
| [[title|id]] | id found | ok | the title text transformed to autolink/tooltip, its autolink/tooltip created based by id and the url belongs to that id |
| [[title|id]] | id not found | error msg | the markdown is included into the HTML output |
| [[title]] | found | ok | the title text transformed to autolink/tooltip, its autolink/tooltip created based by the id of the first matching title in the link collection created by generate_links, and the url belongs to the found id |
| [[title]] | not found | error msg | the markdown is included into the HTML output |
| [[title|-]] | n/a | ok | the exact title text without any modification |
| [[title|]] | n/a | error msg | the markdown is included into the HTML output |
| [[|id]] | n/a | error msg | the markdown is included into the HTML output |
Example results of above cases from top down
existing id with title test
unknown [[id|unknown-id]] test
forced existing source test
forced none existing [[title]] test
forced original text source test
empty [[id|]] test
empty title [[|doc-jekyll-extensions]] test
liquify
liquify.rb is a nice liquid filter that parses liquid objects to their actual value. It is useful e.g. in places where it is not happening automatically, e.g. in page titles.
common_includes
common_includes.rb is an unconditional :site, :pre_render hook (no env-var gate) that prepends `
` to every Markdown and HTML layout, page, and document. It runs in both Jekyll builds (JEKYLL_BUILD_LINKS=yes and JEKYLL_BUILD_TOOLTIPS=yes) so the link extraction and the tooltip injection see exactly the same content. Any change to _includes/doc/common_snippets therefore affects every single page in the site.
extract_headings
extract_headings.rb registers a Liquid filter (extract_headings) that takes a rendered HTML fragment and returns the array of heading anchors found in it (h2/h3/h4 with an id). It is used by _js/lunr/lunr-store.js to add one extra Lunr search-store entry per heading, so a query that exactly matches a section heading (for example ${AMPM} on the macros page) surfaces as its own top-ranked result that deep-links straight to the anchor instead of being buried inside the parent page’s single body-text match.
site_data_by_path
site_data_by_path.rb registers a tiny Liquid filter (data_by_path) that walks a dotted path through a hash and returns the nested value, e.g. {{ site.data | data_by_path: "links.adm-src-network.url" }}. It exists so Liquid templates can dereference _data/... entries dynamically (with a runtime path string) without having to chain [] accessors at template-write time.
sass_silence_deprecations
sass_silence_deprecations.rb is a small monkey-patch on Jekyll::Converters::Scss that propagates a silence_deprecations: array from _config.yml (sass.silence_deprecations) into the sass-embedded runtime, suppressing the matching SASS deprecation warnings (import, global-builtin, etc.) during the build. It does not affect rendered output – only build log noise.
expand_notice_blocks
expand_notice_blocks.rb rewrites the paired {: .notice--TYPE-start} / {: .notice--TYPE-end} markers into <div class="notice--TYPE" markdown="1">…</div> wrappers so a notice can contain lists, fenced code blocks, headings, or multiple paragraphs (the legacy single-block kramdown IAL form {: .notice--TYPE} only attaches to the immediately preceding block). It runs in both builds (no env gate) so link extraction and tooltip injection see identical DOM, skips markers inside fenced code blocks and HTML comments, and aborts the build with a clear file/line error on any pairing violation (mismatched type, missing -end, stray -end, nested -start). For the authoring conventions (typed variants, .no-prefix opt-out, list-first behaviour), see the Notice blocks section below.
Notice blocks
Notice blocks are the gray/colored callout boxes used throughout the documentation. They combine three pieces of machinery:
- the Minimal Mistakes
.notice/.notice--TYPEclasses, - the
_plugins/expand_notice_blocks.rbplugin that expands paired markers into a wrapping<div>, - the
@mixin notice-prefixrule in_sass/minimal-mistakes/minimal-mistakes/_notices.scssthat injects the leading icon and the bold label automatically.
Notice types and CSS-driven icon and label auto-injection
The five typed notice variants get their leading icon and bold label injected automatically by @mixin notice-prefix. Authors must never write the  **LABEL:** prefix in the source — doing so would render the icon and label twice. The plain .notice class has no auto-prefix; any leading label there is written manually.
| Class | Auto-injected icon | Auto-injected label |
|---|---|---|
.notice--primary |
/assets/images/note.png |
NOTE: |
.notice--info |
/assets/images/info.png |
INFO: |
.notice--warning |
/assets/images/caution.png |
WARNING: |
.notice--danger |
/assets/images/warning.png |
DANGER: |
.notice--success |
/assets/images/success.png |
SUCCESS: |
.notice (plain) |
— none — | — none — |
The auto-injected prefix flows inline with the first line of the body text. To change an icon or a label, edit @mixin notice-prefix invocations near the bottom of _sass/minimal-mistakes/minimal-mistakes/_notices.scss.
List-first paired-marker notices. When the first block inside a paired-marker notice is a list (<ol> or <ul>), the icon and label render on the wrapping container’s own line above the list rather than inline with the first list item. This avoids collisions with the 1. / bullet markers. To get an inline prefix instead, lead the body with a paragraph or heading before the list.
Opt-out — .no-prefix modifier
Add .no-prefix alongside the variant class to suppress the auto-injected icon and label for a single notice. Use this only when the body already provides its own visual cue (a custom heading, a bespoke inline icon, embedded HTML, etc.):
Body text rendered without the icon and label prefix.
{: .notice--warning .no-prefix}
Rendered:
Body text rendered without the icon and label prefix.
The paired-marker form supports the same modifier — append it to the start marker:
{: .notice--warning-start .no-prefix}
Multi-block body without the auto-injected prefix.
{: .notice--warning-end}
Rendered:
Multi-block body without the auto-injected prefix.
Single-block form (kramdown IAL)
Attaches to the single preceding Markdown block. Use this for one-paragraph notices.
Important information here.
{: .notice--info}
Critical warning here.
{: .notice--warning}
Rendered:
Important information here.
Critical warning here.
Multi-block form (paired markers)
Use the paired markers expanded by _plugins/expand_notice_blocks.rb when the notice must wrap lists, fenced code blocks, headings, or multiple paragraphs. Each marker must be on its own line:
{: .notice--warning-start}
Leading paragraph.
1. ordered list item
2. another item
```config
fenced code is fine here
```
Closing paragraph.
{: .notice--warning-end}
Rendered:
Leading paragraph.
- ordered list item
- another item
fenced code is fine here
Closing paragraph.
Pairing is strict — any of these aborts the build with a clear error that names the file and line numbers:
- every
*-startmust have a matching*-endof the same type, in source order - nesting is forbidden (a second
*-startwhile another block is still open is an error) - a stray
*-endwithout a matching*-startis an error
Markers inside fenced code blocks and HTML comments are left untouched, so this syntax can be documented verbatim.
For the live rendering test of all five typed variants and the plain .notice, see Notice blocks and inline custom markdown.