Building an article, deploying it through WordPress's native Custom HTML block, and surviving Hostinger's cache layer โ with every screenshot tucked behind the same "More detailed info" disclosure the tool itself uses.
Short version: it keeps the page calm to scan. It does not make the page smaller to download.
Every screenshot on this page is baked directly into the HTML as inline image data โ
the same self-contained approach article-builder.html uses for its own CSS, so
the whole tool works from one file with no server. That means all twenty-three images are
already part of the document the moment it's fetched; putting them behind a toggle
doesn't remove a single byte from that transfer. If you're picturing a real website with
separate image files instead, that version would save real bandwidth from hiding
images โ this single-file version can't, because there's nothing left to fetch later.
What it does save: a closed <details> never lays out or paints its
contents, and browsers generally skip decoding an image until it's actually going to be
shown. So the first paint of this page only has to rasterize the one-line summaries โ
the twenty-three screenshots stay un-decoded dead weight until you click. Fewer big images
competing for the first frame, less scroll to get through, same file underneath.
Build the article โ everything below happens inside article-builder.html, no server, no build step.
Double-click article-builder.html โ it runs entirely in the browser.
If instead you're hitting a hosted copy (e.g. a URL under
deliberatelearners.com/tools/), it may sit behind basic-auth sign-in like
the prompt below โ enter the credentials once and the builder loads as a normal page.
Three panels on the left feed one live preview on the right โ nothing saves until you click Done.
The stacked panels are Article Header, Add a Part, and Parts in this Article. The whole article lives in the page's memory until Done โ build article.html compiles it.
Kicker, Title, and Dek map straight onto the pill, heading, and subtitle you'll see live.
Watch the live preview update field-by-field as you type โ it's the fastest way to catch a typo before it's buried in HTML. Reset / Erase only clears this panel, not the rest of the article.
Zone 1 stays always-visible; Zone 2 stays hidden until clicked โ that's the whole model, one step at a time.
Leave Numbered step selected. Keep the Zone 1 summary to one sentence.
Tick "Show an always-visible command line" only if the step has a literal
command to run โ it renders as its own you type block regardless of whether
the reader ever opens Zone 2.
Zone 2 (and every long text field) accepts light formatting: a blank line starts a new
paragraph, **bold** and `code` work inline, ## makes a
sub-heading, lines starting with - or * become a bullet list,
1. becomes a numbered list, a line starting with $ becomes a
"you type" command, and a fenced ```โฆ``` block becomes a "you'll see" output block.
When the part is ready, click Append to Article.
Click your own toggle right here, before it ever reaches WordPress.
After appending, the live preview shows the step collapsed behind a "More detailed info" toggle โ the exact component wrapping this very paragraph. Click it: it should relabel to "Less info" and reveal Zone 2. This show/hide interaction is the entire reason the tool exists, so get in the habit of testing every toggle you add.
Fill the same form again per step; Done only compiles once every part is appended.
The form resets its fields after each append but keeps building the same article underneath. Skip the command-line checkbox on steps that don't need one. Done โ build article.html is what finally compiles everything, so don't click it until every step is in.
Parts in this article lists every piece in order — reorder with the arrows, or delete with ×.
Each row is matched to its card in the live preview. Every row carries three controls: ↑ and ↓ swap a part with its neighbor (grayed out at whichever end doesn't apply), and × deletes it outright. Moving a part re-renders the live preview immediately — including step numbers, since a Numbered Step that moves past a Text block or Optional Section renumbers itself automatically.
A step tag that was explicitly restarted (see the next two steps below) shows a small
↻ next to its number in this outline — e.g.
STEP 01 ↻ — so you can tell at a glance which "01" is the actual
start of the article and which one is a deliberate restart further down.
Same Zone 1 / Zone 2 model, but accent-colored by purpose โ like the aside above this list.
Switch Add a Part to Optional section for content that isn't a numbered step โ a troubleshooting note, a "why this matters" aside. It adds an Eyebrow label, a Section title, a one-line teaser (Zone 1), and an Accent color picker โ Neutral, Amber for "proceed with care," or Rust for "troubleshooting." Zone 2 holds the full write-up, hidden the same way as a step's detailed info.
A third "Add a Part" tab — always visible, no title, no disclosure toggle, no card.
Not every part of an article is a numbered step or an aside — sometimes you just
need a line of narrative between two steps, or an intro before the first one. Switch
Add a Part to Text block for that: one textarea, parsed
with the exact same formatting rules as a step's Zone 2 (headings, lists, bold, code,
command blocks, and  for images), rendered directly
into the article with no title field and no "More detailed info" toggle to click through.
It's also unstyled on purpose — no card, no border, no background — so it reads as plain body copy flowing between the boxed step and section cards around it, the way an intro paragraph or a closing note would in a normal article.
Continue from the previous step by default, or explicitly restart at 01.
A Text block or an Optional Section can now sit between two Numbered Steps, which raises a question the builder used to answer for you silently: does the next step continue the sequence, or start a new one? Every step form has a Step numbering dropdown for this — Continue from the previous step (the default) keeps counting through whatever's in between, and Restart at 01 begins a fresh sequence right there, useful for something like a separate troubleshooting mini-list later in the same article.
This isn't cosmetic renumbering after the fact — the live preview, the exported HTML, and the outline's "STEP NN" tags all derive their numbers from the same one calculation, so whichever you pick is reflected consistently everywhere, immediately.
A checkbox on the step (or optional section) form โ Media Library URL by default, with a fallback for embedding a file directly.
Images aren't a separate part type โ they're an optional field on whichever part they illustrate, the same way a step can optionally carry an always-visible command line. Check Add an image on a Numbered Step or an Optional Section, and two tabs appear: Media Library URL and Embed a file directly.
The builder doesn't host images itself โ a Custom HTML block just renders whatever
markup you paste into it, so an <img> tag needs a URL WordPress can
actually serve. In wp-admin, go to Media โ Add New, upload the image,
open it, and copy its File URL (something like
https://yoursite.com/wp-content/uploads/2026/07/photo.jpg). Paste that URL
into the Image URL field, and fill in Alt text โ it's
required, not optional: the builder blocks Append until it's filled in.
This Media-Library route is the one to prefer: WordPress generates responsive sizes for
it and the tag ships with loading="lazy", so it behaves like every other
image on the site.
No Media Library access yet, or just prototyping? Switch to Embed a file
directly and pick a local file โ the builder reads it with
FileReader.readAsDataURL() and embeds it as a base64 data URI right inside
the exported HTML, no upload step at all. Reach for this only when you have to, though.
It skips WordPress's responsive sizing and loading="lazy" entirely โ there's
nothing left to defer, the bytes are already sitting in the document โ and it means the
image's full weight travels inside the Custom HTML block's saved content on
every page load: the same "nothing is actually deferred" tradeoff as hiding a
screenshot behind a toggle in a single self-contained file, except here it's living
permanently in the database instead of a one-off page.
Done both downloads article.html and prints the same markup for a clipboard copy.
Once every step and section is in the outline, click Done โ build
article.html. This triggers a download and prints the same markup into a
Final HTML box โ use Copy to clipboard there if you'd
rather not dig through your Downloads folder. The output starts with
<!-- wp:html --> followed by a <style> block, your
confirmation it's pre-shaped for a Gutenberg Custom HTML block โ no separate stylesheet
to install.
Get it into WordPress โ target the core Custom HTML block, built into Gutenberg since WP 5.0. The Elementor HTML widget some docs mention only matters if you already have Elementor Pro; skip it if you don't.
Open article.html in a plain-text editor and copy everything โ the raw markup, not a rendered preview.
In wp-admin, edit the page or post, add a block, and search "Custom HTML" โ a core block, no plugin required.
Paste it all into that one block โ the <style> tag at the top has to travel with the markup it styles.
Use the block's Preview tab, or the full page preview, and click the toggles once here too.
Hard-refresh the live page and click a couple of disclosure panels to confirm they open and close.
This project's history includes a toggle bug that only surfaced under specific timing conditions โ it looked fine in the editor and broke live. An editor preview is not proof.
The Hostinger / LiteSpeed gotcha โ relevant if the site runs on Hostinger's managed WordPress hosting.
Hostinger's managed WP plans typically ship with LiteSpeed Cache active by
default, and its default JS optimization settings โ minify, combine, and defer inline
<script> execution โ are exactly the class of interference that breaks
toggle logic. It's the same failure mode the project's own test suite exists to catch, just
happening at the hosting layer instead of in the code.
Pick whichever fits how much you rely on those optimizations elsewhere.
After publishing, purge the LiteSpeed cache and re-test the live URL โ cached HTML/JS from before your fix will otherwise keep serving the broken version.
Final verification checklist โ mirrors what test_static_articles.py checks locally, done visually here because that script only runs against local files, not live URLs.
Match the symptom to the likely cause before you start guessing.
| Symptom | Likely cause |
|---|---|
| Toggle does nothing, no errors | LiteSpeed deferred or combined the inline script โ exclude the page or disable JS deferral |
| Works in editor preview, not live | Cache serving an old version โ purge LiteSpeed and hard-refresh |
| Layout breaks only on phone | Check the narrow-width (~380px) case specifically โ desktop and mobile use distinct layout logic |