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 --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.

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.