Blog SVGs: move five text nodes clear of the graphic or card edge they were lost behind - #199
Conversation
…ttom node marker Two text nodes were drawn on top of the dark node markers, which share the markers' fill, so the glyphs under them were invisible (1.01:1 and 1.04:1 against the worst ground pixel under their own ink). Geometry only, no palette or wording change: - The right-hand label was centred at x=632, putting its left edge 12px inside the marker at cx=572 r=18 — the label read "hange plan". It now anchors at start, x=608, an 18px gap from the marker edge that mirrors the 19px gap "Audit" already has on the opposite side. Anchoring at start makes the clearance independent of the rendered string width, which matters because the font stack resolves differently per platform. - The footnote caption at y=346 ran under the bottom marker (y 308-344), striking through "workflows". The card grows 388 -> 424 so the caption can drop to y=398, into a band of its own below the "Confirm" label, with 22px of bottom padding to match the 22px above "Request". Both changes are applied to the Simplified sibling in lockstep. Its label cleared the marker by only 6px before and now has the same 18px. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
…e dark card "One business model drives UI, API, automation, Agents, and audit" measured 468.38px at 16px, in a card whose inner width is 360px (440 wide, 40px padding each side). The last 68px hung off the right edge, so "and audit" was pale mint (#b7d6ce) on the cream ground (#e6e3d5) at 1.21:1 and could not be read. The card cannot grow: the arrow ends at x=640 and the card already runs to x=1114 with the same 86px canvas margin the left card has, so widening it enough (+108px of inner width) would either collide with the arrow or collapse the margin. Geometry only, no wording, colour or size change: the same words break onto a second line, subject-verb on the first and the list on the second. The first line keeps its baseline at y=374 and the second sits at y=398, leaving 20px above the pair and 19px below it inside the card. Both lines now score 7.90:1 on the card ground, which is what the old median metric always reported for the line as a whole. The Simplified sibling is not affected and is deliberately untouched: its caption measures 380.5px in the same 440px card, clearing the edge by 19.5px at 7.90:1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
… on the card Box 3's last line, "should it?" answered structurally, measured 184.71px from x=20 in a 200px-wide card. The last 4.7px plus the descender of the final "y" hung off the right edge onto the near-white page ground at 1.63:1, so the word ended mid-air. The card cannot widen: the four cards sit on a 200/40 grid with the yellow connectors occupying the gaps (740-780 immediately to box 3's right), and the row already runs 60..980 of a 1000-wide canvas. A three-line re-flow does not exist either. The box tolerates about 177px per line (box 1's longest line is 177.2px), and every way of breaking the 445.6px of content across three lines while keeping the quoted phrase whole leaves one line at 184.7px or more — measured, not estimated. So the content takes a fourth line and all four cards grow 120 -> 140 to keep the row uniform. Growing the rects moves no text: the only text node that moves is the bottom caption, which follows the dashed return path down by 20px to keep its existing 14px gap. The connectors move to y=180, the new vertical centre of the cards. Same words, same sizes, same colours throughout. The Simplified sibling is deliberately untouched: its longest box-3 line reaches x=150 of 200, well inside the card. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
|
ACCEPT — rendered before and after, and looked at. All four repairs hold at native size:
Union with current Two corrections to the method I endorsed on #188 — both right, and the first one matters a great deal. 1. A visible-vs-hidden raster difference cannot find this defect class. I ruled that the ink mask be built from the per-pixel difference between a text-visible and a text-hidden raster. That fails on precisely the node that motivated this card: 2. The comparator must key on content, not document index. Inserting a line renumbers every node after it, so an index key silently stops comparing rather than reporting drift — a drift check that fails open. Finding that as a real bug in your own first pass, and saying so, is the reason the 0/0/0 drift numbers here are worth anything. On the two grazing cases: agreed, leave them. Both rendered and looked at, both readable, both stay as authored under the stated bar. And you corrected the card: the divider on The dead The measured impossibility proofs are what make the reflows reviewable rather than plausible. Showing that every three-line break of And the cleanest confirmation in the PR: for all five repaired nodes the new worst-ground reading equals what the old median metric reported before the fix. That is exactly what "the whole glyph run now sits on the ground it was meant to" should look like numerically. Landing on Generated by Claude Code |
Fixes #193
Five blog-SVG text nodes were drawn on top of a foreground graphic or past the edge of their card, so a reader could not read the word. All five are repaired with geometry only — no fill, no font size, no font weight and no wording changed anywhere in the corpus.
What was wrong, and what I did
1–3 ·
conversational-app-iteration/cover-en.svg+cover.svgBefore, at native size, the right-hand label read
hange plan. The leadingCwas not covered so much as camouflaged: the label's fill (#173a36) is the same colour as the node marker it was drawn over, so the glyph was painted and then vanished — 1.01:1 against the worst ground pixel under its own ink, while the median metric read a comfortable 12.28. In the same composition the footnote line readobjects, fields, views, ~~workflows~~, permissions…withworkflowsstruck through by the bottom marker.middleat x=632 → left edge 12px inside the markerstartat x=608 → 18px clear of the markerConfirmAnchoring the label at
startrather than re-centring it is deliberate: the clearance then does not depend on the rendered string width, and this file's font stack (-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif) resolves to a different face on every platform. The 18px gap mirrors the 19pxAuditalready has on the opposite side.The footnote could not simply move down —
Confirmsits directly below it — and could not move up without crossing the ring. The card grows 36px so the footnote gets a band of its own, with 22px of padding below it matching the 22px aboveRequest. Nothing else in the card moves: the ring, the markers and the other three labels are positioned from the group origin, not from the card's height.Both changes are applied to the Simplified sibling in lockstep (ruling 4). Its label cleared the marker by only 6px before and now has the same 18px.
4 ·
metadata-not-code-generation/cover-en.svgBefore,
and audithad walked off the dark card onto the cream ground — pale mint#b7d6ceon#e6e3d5, 1.21:1, effectively gone.The line measured 468.38px at 16px in a card whose inner width is 360px (440 wide, 40px padding each side). The card cannot grow: the arrow ends at x=640 and the card already runs to x=1114 with the same 86px canvas margin the left card has, so the +108px of inner width it would need either collides with the arrow or collapses the margin.
So the same words break onto a second line — subject-verb on the first, the list on the second. The first line keeps its baseline at y=374 and the second sits at y=398, leaving 20px above the pair and 19px below it. Both lines now score 7.90:1, which is exactly what the old median metric always reported for the line as a whole — i.e. the whole run is now on its intended ground.
5 ·
ai-wrote-your-app-dare-to-merge/review-loop-en.svgBefore, the final
yofstructurallylanded outside the card, on near-white ground at 1.63:1.The line measured 184.71px from x=20 in a 200px card. The card cannot widen — the four cards sit on a 200/40 grid with the yellow connectors occupying the gaps (740–780 immediately to its right), and the row already runs 60..980 of a 1000-wide canvas.
A three-line re-flow does not exist either, and I measured rather than assumed. The box tolerates about 177px per line (box 1's longest line is 177.2px). Every way of breaking the 445.6px of content across three lines while keeping the quoted phrase
"should it?"whole leaves one line at 184.7px or more:So the content takes a fourth line, and all four cards grow 120 → 140 to keep the row uniform. Growing the rects moves no text — the only text node that moves is the bottom caption, which follows the dashed return path down by 20px to keep its existing 14px gap. The connectors move to y=180, the new vertical centre of the cards.
The two grazing cases — reported, not fixed (ruling 1)
Both were rendered at native size and looked at. In both, a reader can read every word, so under the stated bar they stay as authored and the seat decides.
vibe-coding-technical-debt-2026/debt-curve-en.svg— 1.76:1. This is a stroke edge, not a covered glyph. The red curve crosses only the descender of thep, and the curve's 10px endpoint dot sits behind the bottom oftands. Every letterform ineruptsis complete above the crossing and the word reads normally. Worth noting for whoever picks it up: this node'sfill="#9a6212"attribute is dead — the stylesheet'stext{fill:#173a36}outranks a presentation attribute, so the line renders dark green, not amber. That is a colour question, out of scope here.self-hosted-ai-app-platform/self-hosted-runtime-en.svg— 2.87:1. The blue divider crosses the secondooftools, not the finalsas recorded on the card.governed toolsis fully readable; the text also spans a panel boundary and stays legible on both grounds.Harness sweep
Rebuilt from the method in #188 — worst ground pixel under the node's own ink, across raster scales 1x / 0.667x / 2x, with the old median recorded alongside. It reproduces the card's seven nodes at the card's own ratios and worst-ground hexes (1.01
#163b35, 1.04#5f7773×2, 1.21#e6e3d5, 1.76#993a1e, 2.87#2563eb), which is what validates it as the acceptance instrument.One deliberate mechanism note: a plain full-vs-ground difference cannot find this defect class, because the worst case is text drawn over a graphic of its own colour — the difference there is exactly zero. The ink mask is therefore built by re-rendering every text node in a unique sentinel fill and matching solid-ink pixels to it.
Full corpus, 106 files, before → after at
4cfecea:The five that left the population:
Change plan1.01 → 12.28, the English footnote 1.04 → 4.93, the Simplified footnote 1.04 → 4.93, and the two re-flowed lines, whose halves now score 7.90 (metadata) and 5.90 (review-loop) on their intended card grounds — in every one of these five cases the new worst-ground reading is exactly what the old median metric reported before the fix, which is the cleanest confirmation that the whole glyph run now sits on the ground it was meant to. The four that remain are all deliberate: the two grazing cases above, and #181's two accent-gold?nodes at 1.79 on a flat ground, which are a palette question and not this card.The five position changes are exactly the nodes I set out to move, and no others:
The comparator keys on content, not document index — inserting a line renumbers every node after it, and an index key would have silently stopped comparing those nodes instead of reporting their drift. That was a real bug in my first pass; it is why
review-loopinitially appeared to move 6 nodes when it moves 1.Locale siblings
Every touched file was swept for its sibling and both siblings were measured rather than assumed:
conversational-app-iteration/cover.svg— same defect, fixed in lockstep.metadata-not-code-generation/cover.svg— no defect, deliberately untouched. Its caption measures 380.5px in the same 440px card, clearing the edge by 19.5px at 7.90:1.ai-wrote-your-app-dare-to-merge/review-loop.svg— no defect, deliberately untouched. Its longest box-3 line reaches x=150 of 200.No
viewBoxchanged in any file, so no article layout moves.Gates
All five run through the shared verify lock at
4cfecea, each exit captured before any pipe (output to a file first, sotailcannot launder a red gate into a green$?),git status --porcelainempty:pnpm install --frozen-lockfileLockfile is up to date, resolution step is skippedpnpm checkResult (135 files): 0 errors, 0 warnings, 0 hintspnpm content:lint --published✓ content lint passed (335 files, 44 glossary terms checked)pnpm build[build] 867 page(s) built in 64.21s·Complete!pnpm seo:smokeSEO smoke test passed (866 HTML pages checked)No changeset: this repo has no
.changesetinfrastructure and no changeset gate.Beyond the SVG-level renders, the built article at
/en/blog/conversational-app-iteration/was served fromdistand opened in Chromium — the cover renders correctly in page context with 0 failed requests.🤖 Generated with Claude Code
https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
Generated by Claude Code