Using HTML blocks in your articles
Build accessible, self-contained interactive widgets (tabs, guided flows, copy buttons) inside a HelpDocs HTML block, then reuse them as Clips.
Most days the block editor is all you need. Then a request lands that the standard blocks can’t quite handle: install steps that differ per operating system, a troubleshooting path that changes based on the reader’s plan, a code snippet people keep mistyping. That’s where the HTML block stops being an escape hatch and starts being a genuine tool.
Here’s the part power users love: HelpDocs HTML blocks accept <style> and <script> tags, so you can build real interactive components, not just static markup. This guide gives you four copy-paste widgets that work out of the box, each one accessible, responsive, and scoped so multiple blocks never collide. Build them once, then save them for the whole team.
The HelpDocs block editor already covers headings, callouts, tables, code, and rich embeds natively, so you should never hand-write those. Reach for an HTML block only when a native block can’t express what you need, and reach for a native block every other time.
How do you show different steps per platform?
Install and setup docs often branch by operating system, and stacking three near-identical procedures makes an article twice as long as it needs to be. A tabbed panel keeps everything in one place and shows only what the reader picked. The version below uses proper role="tablist" markup, tracks the active tab with aria-selected, and supports left and right arrow keys.
Note the id="hd-tabs-1" wrapper. Every style rule and every query is scoped to that id, so you can drop a second copy on the same page (with a different id) and the two won’t clash.
<div class="hd-tabs" id="hd-tabs-1">
<div class="hd-tablist" role="tablist" aria-label="Install steps by platform">
<button class="hd-tab" role="tab" id="hd1-t-mac" aria-controls="hd1-p-mac" aria-selected="true">macOS</button>
<button class="hd-tab" role="tab" id="hd1-t-win" aria-controls="hd1-p-win" aria-selected="false" tabindex="-1">Windows</button>
<button class="hd-tab" role="tab" id="hd1-t-lin" aria-controls="hd1-p-lin" aria-selected="false" tabindex="-1">Linux</button>
</div>
<div class="hd-panel" role="tabpanel" id="hd1-p-mac" aria-labelledby="hd1-t-mac" tabindex="0">
<p>Download the <code>.dmg</code>, drag the app to Applications, then launch it from Spotlight.</p>
</div>
<div class="hd-panel" role="tabpanel" id="hd1-p-win" aria-labelledby="hd1-t-win" tabindex="0" hidden>
<p>Run the <code>.exe</code> installer, accept the prompt, and pin the app to your taskbar.</p>
</div>
<div class="hd-panel" role="tabpanel" id="hd1-p-lin" aria-labelledby="hd1-t-lin" tabindex="0" hidden>
<p>Add our apt repository, then run <code>sudo apt install ourapp</code> and start it from your launcher.</p>
</div>
<style>
#hd-tabs-1 { margin: 16px 0; }
#hd-tabs-1 .hd-tablist { display: flex; flex-wrap: wrap; gap: 4px; border-bottom: 2px solid #e5e7eb; }
#hd-tabs-1 .hd-tab { appearance: none; border: 0; background: transparent; font: inherit; cursor: pointer; padding: 10px 16px; color: #4b5563; border-bottom: 2px solid transparent; margin-bottom: -2px; border-radius: 6px 6px 0 0; }
#hd-tabs-1 .hd-tab:hover { background: #f9fafb; }
#hd-tabs-1 .hd-tab[aria-selected="true"] { color: #9d174d; border-bottom-color: #f54599; font-weight: 600; }
#hd-tabs-1 .hd-tab:focus-visible { outline: 2px solid #f54599; outline-offset: 2px; }
#hd-tabs-1 .hd-panel { padding: 16px 4px; }
#hd-tabs-1 .hd-panel[hidden] { display: none; }
</style>
<script>
(function () {
var root = document.getElementById("hd-tabs-1");
var tabs = Array.prototype.slice.call(root.querySelectorAll("[role='tab']"));
function select(tab) {
tabs.forEach(function (t) {
var on = t === tab;
t.setAttribute("aria-selected", on ? "true" : "false");
t.tabIndex = on ? 0 : -1;
root.querySelector("#" + t.getAttribute("aria-controls")).hidden = !on;
});
}
tabs.forEach(function (tab, i) {
tab.addEventListener("click", function () { select(tab); });
tab.addEventListener("keydown", function (e) {
var next = e.key === "ArrowRight" ? tabs[(i + 1) % tabs.length]
: e.key === "ArrowLeft" ? tabs[(i - 1 + tabs.length) % tabs.length] : null;
if (next) { e.preventDefault(); select(next); next.focus(); }
});
});
})();
</script>
</div>
How do you guide readers to the right steps?
Some articles fork on a condition rather than a platform: the plan someone is on, or the exact error they’re staring at. A labelled <select> lets the reader choose, and matching JavaScript reveals only the relevant steps. It’s compact, it works with a keyboard by default, and screen readers announce the label and options cleanly.
This one keys each panel to the option’s value and hides the rest. The <label> is tied to the control with for, so a tap or click on the label focuses the dropdown too.
<div class="hd-guide" id="hd-guide-1">
<label for="hd-guide-1-select" style="display: block; font-weight: 600; margin-bottom: 6px;">Which error are you seeing?</label>
<select id="hd-guide-1-select" style="width: 100%; max-width: 420px; padding: 10px 12px; border: 1px solid #d1d5db; border-radius: 8px; font: inherit;">
<option value="">Choose an error message</option>
<option value="auth">"Authentication failed"</option>
<option value="sync">"Sync stuck at 0%"</option>
<option value="quota">"Quota exceeded"</option>
</select>
<div class="hd-step" data-when="auth" hidden><p>Sign out, clear cached tokens, then sign back in. If it repeats, reset your API key.</p></div>
<div class="hd-step" data-when="sync" hidden><p>Pause the sync, check that the source folder still exists, then resume. Large libraries can take a few minutes.</p></div>
<div class="hd-step" data-when="quota" hidden><p>You've hit your plan's monthly limit. Archive old items or upgrade to lift the cap.</p></div>
<style>
#hd-guide-1 { margin: 16px 0; }
#hd-guide-1 .hd-step { margin-top: 12px; padding: 12px 16px; background: #f9fafb; border-left: 3px solid #f54599; border-radius: 0 8px 8px 0; }
#hd-guide-1 .hd-step[hidden] { display: none; }
#hd-guide-1 select:focus-visible { outline: 2px solid #f54599; outline-offset: 2px; }
</style>
<script>
(function () {
var root = document.getElementById("hd-guide-1");
var select = root.querySelector("select");
var steps = root.querySelectorAll(".hd-step");
select.addEventListener("change", function () {
steps.forEach(function (s) { s.hidden = s.getAttribute("data-when") !== select.value; });
});
})();
</script>
</div>
How do you make code easy to copy correctly?
If your docs include commands or config, a copy button removes a whole class of “it didn’t work” tickets caused by a stray character. This snippet copies the exact text and confirms with a short “Copied” state, so the feedback isn’t color-only. It falls back gracefully and stays keyboard-operable because the trigger is a real <button>.
<div class="hd-copy" id="hd-copy-1">
<pre style="margin: 0; padding: 16px 48px 16px 16px; background: #0f172a; color: #e2e8f0; border-radius: 8px; overflow-x: auto;"><code>npm install @ourteam/cli</code></pre>
<button type="button" aria-label="Copy code to clipboard" style="position: absolute; top: 8px; right: 8px; font: inherit; font-size: 13px; padding: 4px 10px; background: #1e293b; color: #e2e8f0; border: 1px solid #334155; border-radius: 6px; cursor: pointer;">Copy</button>
<style>
#hd-copy-1 { position: relative; margin: 16px 0; }
#hd-copy-1 button:hover { background: #334155; }
#hd-copy-1 button:focus-visible { outline: 2px solid #f54599; outline-offset: 2px; }
</style>
<script>
(function () {
var root = document.getElementById("hd-copy-1");
var btn = root.querySelector("button");
var code = root.querySelector("code").innerText;
btn.addEventListener("click", function () {
navigator.clipboard.writeText(code).then(function () {
var label = btn.textContent;
btn.textContent = "Copied";
setTimeout(function () { btn.textContent = label; }, 1500);
});
});
})();
</script>
</div>
Need a plainer piece too? A static notice bar is worth keeping in your kit for deprecation notes and version flags. It leans on role="note" and puts the meaning in the text, so nothing depends on the red alone.
<div role="note" style="display: flex; align-items: center; gap: 12px; border: 1px solid #fecdd3; background: #fff1f2; color: #9f1239; padding: 12px 16px; border-radius: 8px; margin: 16px 0;">
<span style="flex-shrink: 0; background: #9f1239; color: #ffffff; font-size: 12px; font-weight: 700; padding: 3px 8px; border-radius: 999px;">DEPRECATED</span>
<span>The legacy API sunsets on 1 Sept 2026. Move to <a href="/article/rest-v2" style="color: #9f1239; font-weight: 600;">REST v2</a> before then.</span>
</div>
What keeps interactive blocks from breaking?
A few habits keep these widgets reliable once they’re loose in a real knowledge base. The theme running here is scope and accessibility, because that’s what separates a clever demo from something you can trust across hundreds of articles.
- Scope everything. Wrap each widget in a unique id and prefix its classes, then query only inside that id. Two blocks on one page should never share a selector or a variable.
- Don’t rely on color alone. Pair every colored state with text, an icon, or a shape, and keep focus outlines visible for keyboard users.
- Test at phone width. Fixed widths are the single most common thing that breaks on mobile, so preview narrow before you publish.
- Trust your sources. An HTML block renders exactly what you paste, with no tidy-up pass, so only run scripts you understand.
How do you reuse these across your team?
This is where the productivity compounds. Every widget above is worth building once and reusing forever, so treat them as a shared toolkit rather than one-off code. Save each polished version as a Clip, name it clearly (tabs-platform, guide-troubleshoot, code-copy, banner-deprecated), and your whole team inserts the same tested markup instead of each person shipping a slightly different box.
Dynamic Clips go one better: fix a bug or update a color in the canonical Clip, and it updates everywhere it’s used at once. Our guide on how to save widgets as reusable Clips walks through setting that up. Pair it with the standards in your documentation style guide so the colors, spacing, and labels stay consistent no matter who builds the next block.
Used this way, HTML blocks stop being a fiddly last resort and become a small library of on-brand, interactive parts you assemble in seconds. Build it once, make it accessible, and reuse it everywhere.