Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/css/mermaid.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
text-align: center;
}

/* Mermaid SVGs have no internal bottom padding, so the figure caption
needs more separation than a regular image caption. */
.doc .imageblock .mermaid + .title {
margin-top: 1.25rem;
}
Comment on lines +7 to +11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C 6 'mermaid|imageblock|class="title"|class="content"' . \
  --glob '*.hbs' --glob '*.adoc' --glob '*.html' --glob '*.js'

Repository: redpanda-data/docs-ui

Length of output: 20505


🌐 Web query:

Antora asciidoctor asciimath mermaid block imageblock generated HTML .mermaid .content .title

💡 Result:

In Antora, Mermaid diagrams are not supported out-of-the-box by Asciidoctor and require specific extensions [1][2][3]. There are two primary ways to handle them: via the @sntke/antora-mermaid-extension (client-side rendering) or via extensions like asciidoctor-kroki (server-side rendering) [1][4]. Client-side rendering with @sntke/antora-mermaid-extension: This extension preserves the Mermaid source in the generated HTML [1][5]. When a [mermaid] block is processed, it typically outputs a block element (often a div or pre) assigned the CSS class.mermaid [5]. The structure of the generated HTML generally includes:.mermaid: The container element for the diagram [5]..content: Within the container, the raw text definition of the Mermaid diagram is preserved [5]..title: If a title is provided for the block in AsciiDoc (e.g.,.Diagram Title), it is rendered by Asciidoctor and typically appears as a separate element (e.g.,.title) associated with the block [5]. Configuration: To use this, you must add the extension to your antora-playbook.yaml under the antora.extensions section, not the asciidoc.extensions section [1][6]. You will also need to ensure the Mermaid.js library is loaded via your UI bundle's scripts [1][7]. Server-side rendering with Kroki: Alternatively, using asciidoctor-kroki allows for rendering diagrams into image files (e.g., SVG or PNG) at build time [8][4]. In this approach, the output is typically a standard AsciiDoc block image [9]. This is configured by adding asciidoctor-kroki to the asciidoc.extensions list in your playbook [4]. Important Note: If you are using the @sntke/antora-mermaid-extension, be aware that misconfiguration of attributes in your antora.yml can sometimes interfere with block processing [5]. Always ensure you are targeting the correct extension facility (Antora vs. Asciidoctor) based on your chosen rendering method [6].

Citations:


Target the direct children when matching Mermaid titles.

The current selector depends on .title being the immediate sibling of .mermaid; if the rendered block contains a sibling child wrapper before the title, the spacing rule won’t apply. Use a more direct structural selector, such as .doc .imageblock .mermaid + .title, to reduce reliance on sibling ordering without losing the intended target.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/css/mermaid.css` around lines 7 - 11, Update the Mermaid caption selector
in the `.doc .imageblock` rule to match `.title` elements that follow `.mermaid`
even when an intervening sibling wrapper exists, while keeping the match scoped
to the image block and preserving the existing margin.


.mermaid > svg {
background-color: var(--body-background) !important;
max-width: 100%;
Expand Down
3 changes: 3 additions & 0 deletions src/js/07-expand-images.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
if (!blocks.length || !modalOverlay) return

blocks.forEach((block) => {
// Mermaid diagrams have their own zoom modal (20-mermaid-zoom.js).
// Binding both opens two stacked modals for one click.
if (block.querySelector('.mermaid')) return
block.addEventListener('click', function (e) {
const media = block.querySelector('img, svg')
if (!media) return
Expand Down
77 changes: 77 additions & 0 deletions src/js/08-mermaid-label-spacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
;(function () {
'use strict'

// Mermaid (v10 and v11) never reserves vertical space between a subgraph
// label and nested subgraphs: the label overlaps the inner boxes' top
// borders, and neither flowchart.subGraphTitleMargin nor flowchart.padding
// moves the children. Fix the rendered SVG instead: shift the label up and
// grow the cluster rect (and the viewBox for top-level clusters) to match.

const MIN_GAP = 12

if (!document.querySelector('.mermaid')) return

function fixSvg (svg) {
const clusters = Array.from(svg.querySelectorAll('g.cluster'))
let viewBoxGrow = 0
clusters.forEach(function (cluster) {
const rect = cluster.querySelector('rect')
const label = cluster.querySelector('.cluster-label')
if (!rect || !label) return
const rx = parseFloat(rect.getAttribute('x'))
const ry = parseFloat(rect.getAttribute('y'))
const rw = parseFloat(rect.getAttribute('width'))
const rh = parseFloat(rect.getAttribute('height'))
const m = (label.getAttribute('transform') || '').match(/translate\(\s*([-\d.]+)[ ,]\s*([-\d.]+)\s*\)/)
if (!m) return
const lx = parseFloat(m[1])
const ly = parseFloat(m[2])
let lh
try {
lh = label.getBBox().height
} catch (e) {
return // not rendered (display: none); leave untouched
}
// Find the highest cluster rect fully contained in this one
let minChildTop = Infinity
clusters.forEach(function (other) {
if (other === cluster) return
const or = other.querySelector('rect')
if (!or) return
const ox = parseFloat(or.getAttribute('x'))
const oy = parseFloat(or.getAttribute('y'))
const ow = parseFloat(or.getAttribute('width'))
const oh = parseFloat(or.getAttribute('height'))
const contained = ox >= rx - 1 && ox + ow <= rx + rw + 1 && oy > ry && oy + oh <= ry + rh + 1
if (contained && oy < minChildTop) minChildTop = oy
})
if (minChildTop === Infinity) return
const gap = minChildTop - (ly + lh)
if (gap >= MIN_GAP) return
const delta = Math.ceil(MIN_GAP - gap)
label.setAttribute('transform', 'translate(' + lx + ', ' + (ly - delta) + ')')
rect.setAttribute('y', ry - delta)
rect.setAttribute('height', rh + delta)
viewBoxGrow = Math.max(viewBoxGrow, delta)
})
if (viewBoxGrow > 0) {
const vb = (svg.getAttribute('viewBox') || '').split(/[ ,]+/).map(Number)
if (vb.length === 4 && vb.every(function (n) { return !isNaN(n) })) {
vb[1] -= viewBoxGrow
vb[3] += viewBoxGrow
svg.setAttribute('viewBox', vb.join(' '))
}
}
svg.setAttribute('data-label-spacing', 'fixed')
}

// Mermaid renders asynchronously after its module loads; poll briefly.
let attempts = 0
const timer = setInterval(function () {
attempts++
const pending = document.querySelectorAll('.mermaid svg:not([data-label-spacing])')
pending.forEach(fixSvg)
const remaining = document.querySelectorAll('.mermaid:not([data-processed])').length
if (remaining === 0 || attempts > 100) clearInterval(timer)
}, 200)
})()
2 changes: 0 additions & 2 deletions src/partials/feedback-forms.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,4 @@
</div>
</div>
</div>

<div class="modal-overlay" id="modal-overlay"></div>
</form>
Loading