Plugins and scripts
The site adds a few scripts of its own to Docsy’s, under assets/js/, loaded
three ways:
- Plugins, through Docsy’s loader
- Hook scripts, through the body-end hook (inventory) and the head-end hook (one script, the Kapa widget’s pre-initialization)
- Shortcode scripts, by the shortcode of the feature that needs them: the registry search form and the config-types accordion, for example
Hooks and shortcodes load a script through the script.html partial: Hugo’s
js.Build bundles each file, and production builds also minify, fingerprint and
add subresource integrity.
Plugins
Site plugins follow Docsy’s custom-script contract under the
otel- prefix. One that renders markup of its own is gated by a shortcode that
owns the markup and marks the page, so the gate follows the content into every
language that serves it; the shim reads the mark. A shortcode in included
content marks the included page, not the page that includes it (Page flags in
included content).
Benchmarks plugin
The otel-benchmarks plugin is a fork of the renderer that
github-action-benchmark publishes with a repository’s benchmark history (a
data.js on GitHub Pages); the action defines the data’s shape in its source
types and reads it in that stock renderer. The shortcode call names the
data and says how to read it:
{{< benchmarks data_url="param:otel-benchmarks.data_url" filter="10kDPS,10kSPS" group_separator="/" series_separator=" - " >}}
data_url: the action’sdata.js, ashttps://…or asparam:NAME, a page param holding it; the Collector page keeps the URL inparams.otel-benchmarks.data_urlbecause its prose links it too, through_paramfilter: keep a bench whoseextracontains any of the comma-separated stringsgroup_separator: splitextrainto a group, joined to the bench name as the chart’s title, and a series name; anextrawithout the separator is bothseries_separator: trim a series name split off bygroup_separatorat its first occurrence
Without group_separator, each bench name is its own chart with one series,
which assumes bench names are unique within a commit. Every chart shares the
data set’s commit axis; the stock renderer, which the JavaScript and Python
pages still carry inline, gives each bench only its own commits. The plugin’s
parts:
- Shortcode,
layouts/_shortcodes/benchmarks.html: emits the chart container, adivwhosedata-otel-benchmarksattribute carries the call’s options as JSON for the renderer, and records the data URL in the page store asotelBenchmarks. A malformeddata_url, an unknown or positional argument, or a second call naming other data fails the build. Links and text belong in the page’s prose, where each language writes its own. Each call renders a chart set where it stands. - Markdown variant,
benchmarks.mdbeside it: empty by design, so the HTML template’s markup stays out of the page’s Markdown output, which the site serves per Agent support; the data file the prose links is an agent’s path to the numbers. - Shim,
layouts/_partials/scripts/plugins/otel-benchmarks_docsy-shim.html(Adjust a plugin per page): disables the plugin on pages without the record, which keeps the remote data script off every other page (Security). - Companion,
layouts/_partials/scripts/plugins/otel-benchmarks.html(Plugin files): vendors Chart.js on the entry’sversionpin, then loads the data file from the record. The data stays a live remote script so the charts update between site builds, which a fixed integrity hash would forbid. - Script,
assets/js/plugins/otel-benchmarks/(Plugin files):index.jsis the renderer; beside it,legend.js(the legend’s shape and the chart height that fits it),commit.js(a point’s value line and commit, in the tooltip and the popover) andpoint-popover.js(the click popover). Docsy resolvesjs/plugins/otel-benchmarks.jsonly, so that file imports the folder’s entry. - Stylesheet,
assets/scss/plugins/otel-benchmarks.scss(Plugin files): the charts’ layout, scoped to the container (a horizontal scroller per chart for narrow columns, the minimum width the renderer sizes each chart from, and spacing); Docsy emits it with the plugin. The inline copies keep the stock page’sstatic/css/benchmarks.css, whose baremain,buttonandfooterselectors also reach Docsy’s chrome. - Registration,
config/_default/hugo.yaml(Configuration reference): one site-level entry, since the shim decides per page. - Tests,
tests/public/otel-benchmarks.test.mjs: the built pages across converted, unconverted and fallback languages, and the Markdown output of the converted ones.
Convert a localized page
To convert a localized copy that still inlines the script:
- Remove the stylesheet link above the prose, and replace the block below it
(from the
markdownlintcomment through the last<script>tag) with the shortcode line from the English page. - Copy the English page’s
otel-benchmarksparam, translate its tip, and link the data from the prose as it does. - Add the language to
CONVERTEDintests/public/otel-benchmarks.test.mjs.
Languages without their own copy serve the English page and convert with it.
Body-end scripts
| Script | Pages | Purpose |
|---|---|---|
blog-sidebar-entry-opener.js | Blog index and pager pages (acts on the first) | Opens the two newest year groups in the sidebar |
imageZoom.js | All | Click-to-zoom for content images |
navScroll.js | All | Meant to scroll the sidebar’s active entry into view; inert today, since the active state is applied after it runs |
tracing.js | Netlify builds, when unpaused | Website telemetry, paused |
Blog sidebar entry opener
blog-sidebar-entry-opener.js is the one body-end script gated to a page and
the one that acts on the sidebar.
Gating to the blog index
The body-end hook renders on every page, so the opener is gated there on the
page’s .Path, which is the same in every locale: {{ if eq .Path "/blog" }}.
A .Path gate can’t single out the first page of a paginated list. Hugo renders
a list’s pager pages from the one section page, so .Path, .RelPermalink and
.Pages are identical on /blog/ and /blog/page/2/, and the current slice is
known only to the pager (.Paginator, or what .Paginate returned). Calling
.Paginator on a list page that Docsy’s template leaves unpaginated, such as an
empty blog, paginates it and publishes a page/1/ alias. The opener therefore
loads on every pager page and skips them at runtime, by testing
location.pathname for /page/.
Acting on the sidebar
The site uses Docsy’s cached sidebar (ui.sidebar_cache_limit: 1): one nav per
sidebar root, shipped hidden (d-none) with no active state. Docsy’s
chrome-nav.js hydrates it at DOMContentLoaded: it applies the active state
and reveals the nav. The blog index, like every section’s landing page, carries
that nav inline in both build kinds, so a body-end
script there runs during parsing, before the reveal, and can set sidebar state
before the sidebar appears; the opener checks its two year groups then. In a
lean build, other pages carry a placeholder instead, which chrome-nav.js fills
after fetching the nav, too late for a body-end script. With JavaScript
disabled, the sidebar stays hidden.