9. The HTML Output Layer

Subsections in this chapter are informative unless normativity is explicitly stated.

9.1. Introduction

The command waterlint render-html5 converts a Waterloo JSON document into a bundled, human-readable HTML5 artifact. The input is therefore not a Python module and not a Waterloo docstring directly, but a JSON document as described in chapter The JSON I/O Layer.

The result is a single HTML file. CSS and JavaScript are embedded directly into that file, so no additional assets need to be deployed alongside it. In practice, this has proven to be very convenient, because the generated documentation can be opened, shared, and used offline without further setup.

At the moment, the HTML5 renderer is part of the reference tooling. Conceptually, however, it should be understood as an output layer. It is therefore possible that this functionality will later evolve toward a plugin-like architecture with interchangeable renderers.

9.2. Rendering HTML5 Documentation

The basic call is

waterlint render-html5
--in path/to/input.json
--out path/to/output.html

Instead of --out, option --out-dir may be used:

waterlint render-html5
--in path/to/input.json
--out-dir path/to/output-dir/

In this case, waterlint generates a filename based on the input metadata, in particular the scope and flavour stored in the JSON document.

The following options are particularly relevant:

--css path/to/custom.css --additional-css path/to/additional.css --pygments-theme theme-name --pygments-dark-theme theme-name --no-render-preamble

Option --css replaces the built-in default stylesheet with a custom CSS file. This is useful if the full visual presentation should be controlled externally. If --additional-css is also given, the additional stylesheet is appended after the CSS provided via --css.

Option --additional-css appends the given CSS file after the primary stylesheet. If --css is not given, the built-in default stylesheet acts as the primary stylesheet. This is useful if a shared base stylesheet should be adapted to a project-specific visual style.

Option --pygments-theme selects the syntax-highlighting theme used for embedded code examples in light mode. Option --pygments-dark-theme selects the corresponding theme for dark mode. When the page theme is set to auto, the generated CSS follows the system color-scheme preference.

Option --no-render-preamble suppresses the section Preamble: in the generated HTML output. This can be useful when the output is intended primarily for human readers who do not need to see the full structural metadata of the underlying Waterloo document.

Diagnostics are written in human-readable form by default. The output locations for human- and machine-readable diagnostics are specified by

--out-diag path/to/diagnostics --out-diag-json path/to/diagnostics.json

A summary of these options is displayed by

waterlint help --topic render-html5

Example

Assume the JSON document from chapter The JSON I/O Layer has already been generated and is located at

doc/output-json/mypkg.test_module_minimal.with_examples.wtrl.core.rfc-2119.json

We can then render the interactive HTML5 output by

waterlint render-html5
--in doc/output-json/mypkg.test_module_minimal.with_examples.wtrl.core.rfc-2119.json
--out doc/output-html/mypkg.test_module_minimal.with_examples.wtrl.core.rfc-2119.html

The generated artifact is then located at

doc/output-html/mypkg.test_module_minimal.with_examples.wtrl.core.rfc-2119.html

If the visual style should be adapted, an additional stylesheet can be embedded:

waterlint render-html5
--in doc/output-json/mypkg.test_module_minimal.with_examples.wtrl.core.rfc-2119.json
--out doc/output-html/mypkg.test_module_minimal.with_examples.wtrl.core.rfc-2119.html
--additional-css path/to/additional.css

9.3. Adding a custom header

The header area above the rendered documentation can also be customized by passing an HTML fragment file:

waterlint render-html5
--in doc/output-json/mypkg.test_module_minimal.with_examples.wtrl.core.rfc-2119.json
--out doc/output-html/mypkg.test_module_minimal.with_examples.wtrl.core.rfc-2119.html
--header-html doc/input-html/test_header_minimal.html

The fragment must contain an element with ID wtrl-title, see rule RTHM-008 in Rendering HTML (render-html5). This element is populated dynamically with the qualified identifier of the currently selected object. An element with ID wtrl-sub is optional and, if present, is used for a subtitle.

For example:

<div id="wtrl-header" class="wtrl-header">
  <h1 id="wtrl-title" class="wtrl-title"></h1>
  <p id="wtrl-sub" class="wtrl-sub"></p>
</div>

If the header fragment refers to external assets, for example via img src="...", the single-file property of the generated HTML documentation is lost. If this property should be preserved, small assets such as project logos can be embedded directly into the header fragment as data: URIs. For binary formats such as PNG, this is typically done with data:image/png;base64,....

On Unix-like systems, the Base64 payload can conveniently be generated on the command line. This approach is practical for small logos and keeps the resulting documentation self-contained and usable offline.

A more elaborate example with embedded logo and project link is shown in

doc/input-html/test_header_tde4.html doc/input-html/test_header_tde4.css

and can be used as follows:

waterlint render-html5
--in doc/output-json/mypkg.test_module_minimal.with_examples.wtrl.core.rfc-2119.json
--out doc/output-html/mypkg.test_module_minimal.with_examples.wtrl.core.rfc-2119.html
--header-html doc/input-html/test_header_tde4.html
--additional-css doc/input-html/test_header_tde4.css

Header fragment:

<div id="wtrl-header" class="wtrl-header-card">
  <div class="wtrl-header-brand">
    <img
      class="wtrl-header-logo"
      alt="Waterloo Docstrings Logo"
      src="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 96 96'><rect width='96' height='96' rx='20' fill='%230f766e'/><circle cx='48' cy='48' r='28' fill='%23ffffff' opacity='0.14'/><path d='M28 58c8-18 15-28 20-28 6 0 10 9 20 28' fill='none' stroke='%23ffffff' stroke-width='8' stroke-linecap='round'/><path d='M34 38h28' fill='none' stroke='%23ffffff' stroke-width='8' stroke-linecap='round'/></svg>"
    >
    <div class="wtrl-header-copy">
      <div class="wtrl-header-kicker">Interactive API documentation</div>
      <h1 id="wtrl-title" class="wtrl-title"></h1>
      <p id="wtrl-sub" class="wtrl-sub"></p>
    </div>
  </div>
  <div class="wtrl-header-meta">
    <a class="wtrl-header-link" href="https://github.com/uwe-at-sdv/sdv_doc_waterloo">
      Waterloo Docstrings on GitHub
    </a>
  </div>
</div>

Additional CSS:

.wtrl-header-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem 1.5rem;
  margin: 0 0 1rem 0;
  padding: 1rem 1.25rem;
  border: 1px solid #d7dee7;
  border-radius: 0.75rem;
  background:
    radial-gradient(circle at top left, #eef6ff 0, #eef6ff 16%, transparent 40%),
    linear-gradient(135deg, #ffffff 0%, #f7fafc 100%);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}

.wtrl-header-brand {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-width: 0;
}

.wtrl-header-logo {
  width: 48px;
  height: 48px;
  flex: 0 0 auto;
  border-radius: 0.6rem;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.12);
}

.wtrl-header-copy {
  min-width: 0;
}

.wtrl-header-kicker {
  margin: 0 0 0.2rem 0;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #0f766e;
}

.wtrl-header-meta {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.wtrl-header-link {
  display: inline-flex;
  align-items: center;
  padding: 0.55rem 0.85rem;
  border: 1px solid #cbd5e1;
  border-radius: 999px;
  background: #ffffff;
  color: #1d4ed8;
  text-decoration: none;
  font-size: 0.92rem;
  font-weight: 600;
}

.wtrl-header-link:hover {
  background: #eff6ff;
}

9.4. Implementation Notes

The current implementation is distributed across three components:

  • the Python command implementation in waterlint_render_html5.py

  • the JavaScript asset in js/waterlint_render_html5.js

  • the CSS stylesheet in css/wtrl-style.css

The Python layer prepares the bundled HTML document and injects the data. The JavaScript layer provides the interactive behaviour in the browser, for example search, navigation, and dynamic rendering of object details. The CSS layer defines the visual presentation.

This separation is intentional. It keeps the current implementation maintainable and also makes a later transition toward a more explicit plugin or renderer architecture easier.

9.5. CSS Reference

This informative section reflects the implementation as of 2026-09-24.

This section defines the supported CSS customization surface of the bundled HTML5 output. It documents CSS custom properties and selected structural classes that are intended as extension hooks. Other selectors in the bundled stylesheets are implementation details and may change when the renderer evolves.

The built-in stylesheet is split into a common set of semantic variables in css/common_styles.css and HTML5-specific layout rules in css/wtrl-style.css. An additional stylesheet passed through --additional-css is the recommended way to override these values.

9.5.1. Theme selection

The generated document records the selected theme on its root HTML element:

  • html[data-wtrl-theme="light"] selects the light palette.

  • html[data-wtrl-theme="dark"] selects the dark palette.

  • html[data-wtrl-theme="auto"] follows the browser’s prefers-color-scheme setting.

Light-mode variables are the active variables used by the layout rules. Dark mode rebinds these active variables to corresponding dark values. This allows a customization stylesheet to either replace a palette value globally or to override an active value only for a selected theme.

9.5.2. Custom property naming

Semantic inline roles use the following family:

--wtrl-<role>-color
--wtrl-<role>-font-weight
--wtrl-<role>-font-style
--wtrl-dark-<role>-color

The role names currently are plain, attr, class, cmd, dfn, file, func, key, label, lit, mod, norm, op, opt, pkg, tag, term, type, url, url-schema, value, and var. The special url-schema role currently has a color variable only.

For semantic roles with a dark palette entry, the light color --wtrl-<role>-color is active by default. Dark-mode selectors replace it with --wtrl-dark-<role>-color. Font weights and styles are currently shared by both themes. The generic plain role uses currentColor and has no dark counterpart.

HTML5-specific variables use a closely related, but intentionally distinct, naming convention:

--wtrl-html-<name>
--wtrl-html-dark-<name>

The active light-mode names and their purposes are listed below. Each has a dark counterpart formed by inserting dark- after html-.

HTML5 color custom properties

Light-mode property

Purpose

--wtrl-html-page-bg

Page background.

--wtrl-html-page-color

Default foreground text color.

--wtrl-html-panel-bg

Sidebar and main-panel background.

--wtrl-html-panel-border-color

Sidebar, panel, and code-block borders.

--wtrl-html-debug-bg

Debug reference area background.

--wtrl-html-muted-color

De-emphasized text such as metadata.

--wtrl-html-subtle-color

Secondary metadata text.

--wtrl-html-control-bg

Input and button background.

--wtrl-html-control-color

Input and button foreground.

--wtrl-html-control-border-color

Input and button borders.

--wtrl-html-control-disabled-bg

Disabled control background.

--wtrl-html-search-border-color

Search field border.

--wtrl-html-hitlist-bg

Search-result list background.

--wtrl-html-hit-border-color

Search-result separators.

--wtrl-html-hit-hover-bg

Hovered search result and theme-control background.

--wtrl-html-section-bg

Section background.

--wtrl-html-section-border-color

Section borders.

--wtrl-html-section-head-color

Section heading color.

--wtrl-html-subsection-head-color

Subsection heading and table-group-title color.

--wtrl-html-code-bg

Embedded example-code background.

--wtrl-html-key-border-color

Border for rendered keyboard keys.

--wtrl-html-grid-border-color

Table cell grid lines.

--wtrl-html-fallback-color

Fallback color for objects without a more specific kind.

The common stylesheet also provides --wtrl-monospace-font-family for code-like text and the three theme-symbol variables --wtrl-theme-light-symbol, --wtrl-theme-auto-symbol, and --wtrl-theme-dark-symbol. Their dark-mode counterparts are named --wtrl-dark-theme-<mode>-symbol.

Minimal customization example

The following stylesheet adjusts only the panel and table-grid palette while leaving all layout rules intact:

:root {
        --wtrl-html-panel-bg: #fcfcf8;
        --wtrl-html-grid-border-color: #c8c8bb;
}

html[data-wtrl-theme="dark"] {
        --wtrl-html-panel-bg: #20231f;
        --wtrl-html-grid-border-color: #536050;
}

9.5.3. Structural class hooks

The following classes are intended for project-specific layout adjustments:

  • .wtrl-app, .wtrl-side, .wtrl-main, and .wtrl-block identify the overall application layout.

  • .wtrl-section, .wtrl-section-head, .wtrl-subsection, and .wtrl-subsection-head identify generated documentation structure.

  • .wtrl-text and .wtrl-list identify free-form textual content and generated lists.

  • .wtrl-table, .wtrl-table-group-title, .wtrl-table-header, and .wtrl-table-row identify Waterloo table blocks.

The renderer also attaches semantic role classes such as .wtrl-func, .wtrl-type, and .wtrl-var to inline markup. Prefer the custom properties above for global role styling; use role classes only when a local selector is required.

9.5.4. Visual reference

Todo

Screenshots are useful as examples of the intended visual result, especially for comparing light and dark themes or documenting a project-specific stylesheet. They are not normative and do not replace the custom-property and class contracts above. A small, annotated set of screenshots is preferable to trying to catalogue every renderer state.