Postmortem: TypeIt broken by Mermaid in LoveIt theme

TL;DR
The LoveIt theme’s typewriter animation (TypeIt) stopped working on the home after adding Mermaid diagrams to posts. Cause: a #id-1 DOM selector shared between both libraries. When Mermaid finds an orphan block in a home summary, its initialization crashes, and the JS init chain stops before reaching TypeIt. Fix: add `
` before any Mermaid block to keep it out of the summary. 16 posts affected in the end.
This postmortem complements Grav to Hugo migration where I briefly mentioned the bug. Here, the detailed diagnostic story and the broader lesson on JS conflicts in Hugo themes.
The symptom
On my arleo.eu home, below my avatar, text scrolls in a typewriter effect. That’s the LoveIt typewriter, configured via params.home.profile.typeit in hugo.toml. It cycles through 3 phrases I thought were cool:
[params.home.profile]
typeit = true
[params.typeit]
speed = 100
cursorSpeed = 1000
cursorChar = "|"
duration = -1On the FR version, it worked. On the EN version, the text appeared frozen. As if TypeIt didn’t trigger.
First reflex: browser devtools. Console: Uncaught TypeError: Cannot read properties of undefined (reading 'initialize') on /en/ load. Stack trace: the failing function is mermaid.initialize(). And right after, in LoveIt’s init chain, the initTypeit() function is never called.
So Mermaid crashes on load, stopping JS init before TypeIt. But why does Mermaid crash on the home? The home isn’t supposed to contain diagrams.
The diagnostic
HTML inspection of the EN home. In a post summary listed on the home, I find a <div class="mermaid"> block right in the middle of an article’s summary. How did it end up there?
LoveIt generates a post summary two ways:
- If the post contains
<!--more-->, summary = what’s before that marker - Otherwise, summary = the first ~70 words of the content
The incriminated post was /en/posts/postmortem-mcp-timeouts-cloudflare/. Its content starts with an intro paragraph, then directly a Mermaid diagram. Without <!--more--> separator, LoveIt takes the first 70 words of the content — which include the Mermaid block in raw format.
But the rendered HTML of the summary preserves the class="mermaid" AND an id="id-1". So Mermaid scans the DOM, sees this block with class="mermaid", tries to initialize it.
The crash
Mermaid initializes via a script that:
- Scans all elements with
class="mermaid" - Gets their text content
- Parses it as Mermaid syntax
- Generates the corresponding SVG
- Replaces the element content with the SVG
On the home, the orphan Mermaid block contains truncated summary text that looks like Mermaid but isn’t valid. Mermaid throws an exception. Not caught. Init stops.
And TypeIt init, launched right after by the same LoveIt JS bundle, never gets called. So the typewriter stays silent.
The final symptom is very far from the cause. No visible error on the content side, no Hugo build error, no Mermaid syntax error (the original diagram in the article is correct). It’s the HTML summary that contains a corrupted version of the block.
The fix
Trivial once the cause is identified: add <!--more--> before the first Mermaid block of each affected post.
---
title: My technical post
---
Intro paragraph that will appear in the summary.
<!--more-->
The rest of the content, including Mermaid blocks.Once the marker is added, the summary becomes just the intro, without Mermaid. The home no longer has an orphan class="mermaid" block. Mermaid no longer tries to initialize something invalid. JS init continues to the end, TypeIt starts, the typewriter works.
The scope
How many posts affected? Detection script:
for f in $(find content/posts -name "*.md"); do
if grep -q '```mermaid' "$f"; then
if ! grep -q '<!--more-->' "$f"; then
echo "Missing <!--more--> in $f"
fi
fi
doneVerdict: 16 files (8 FR posts + 8 EN, since all my recent posts are bilingual). All fixed in one pass with an idempotent Python script.
Why on EN and not FR at first?
Good question. The bug existed in both languages, but the FR home visibly had TypeIt working. Deeper inspection: it’s the order of posts in the home that made the difference. On FR, the first posts didn’t have Mermaid (so summaries OK). On EN, the first post in the listing had an orphan Mermaid (so summary KO).
LoveIt initializes components in the order it finds DOM elements. As long as Mermaid finds a valid block before a broken one, init crashes after TypeIt and breaks nothing. If it crashes before, it stops the chain.
So the bug was latent on FR too. The <!--more--> on the 16 posts is a real fix, not a cosmetic patch.
The broader lesson
Hugo themes that load multiple JS libs via a single bundle ALL have this kind of risk: a lib that crashes stops the others. Without try/catch around each init, you have a fragile chain.
Patterns to remember for anyone writing a Hugo theme (or any template):
1. Always wrap JS inits
// Bad
mermaid.initialize({startOnLoad: true});
typeit.init('#typeit-target');
// Good
try { mermaid.initialize({startOnLoad: true}); } catch (e) { console.error('mermaid init failed:', e); }
try { typeit.init('#typeit-target'); } catch (e) { console.error('typeit init failed:', e); }A JS init has no reason to propagate its error to other libs in the same bundle.
2. Beware of auto-generated selectors
LoveIt names its elements id-1, id-2, etc. If another component uses the same IDs (or if Mermaid scans [class="mermaid"] instead of .mermaid:not(.processed)), conflicts guaranteed. Better: use prefixed classes/IDs (.lv-typeit, .lv-mermaid-container).
3. Hugo build-side validation
One could imagine a Hugo test that parses all .md files containing Mermaid blocks, verifies they have <!--more--> BEFORE the first block, and fails the build otherwise. Not implemented for now, but it would prevent the bug from reappearing when forgetting the marker.
4. Automated visual tests
For sites relying on visual effects (typewriter, animations, lazy loading), a Playwright test that loads the home and verifies TypeIt has animated would be precious. Even better: screenshot diffing.
Upstream to LoveIt
I opened an issue on the LoveIt repo with the diagnostic. The maintainer could:
- Wrap inits in try/catch (minimal fix)
- Strip
class="mermaid"blocks from summary HTML (robust template-side fix) - Document explicitly that
<!--more-->is required for posts with Mermaid (documentary fix)
The 3rd would be quickest to put in place, but the 2nd remains the clean solution.
Conclusion
A bug with distant side effects (the home doesn’t typewriter) caused by a detail far from the symptom (an orphan block in a summary). The diagnostic took ~30 minutes once the browser devtools were open. The lesson: when a visible JS behavior fails, opening the console is the first step, even if the content seems unrelated.
16 files fixed in minutes via script. The <!--more--> marker is now systematic in my post templates.
If you use LoveIt with Mermaid, check your posts. The bug is latent on any post without an explicit marker.