Files
reliquary/plugins/g4b_ai/skills/markdown-embedded-svg/references/layout-legibility.md
T
g4borg 61af13521c 🤖 Restructure SVG skill: extract references, add legibility rules
Extract inline SVG rules to references/inline-svg.md for progressive
disclosure. Add references/layout-legibility.md with 7 rules for
readable SVG output (text sizing, contrast, margins, chart patterns,
palettes) based on concrete failure modes from issue #4.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-19 20:46:12 +02:00

4.8 KiB
Raw Blame History

Layout and Legibility Rules for SVG Diagrams

These rules prevent the most common legibility failures in agent-generated SVG: text overflow, poor contrast, and missing margins. They apply to both external .svg files and inline SVG.

Rule 1 — Estimate max-text-width before sizing a container

For any text that lives inside a bounded shape (cell, box, button), estimate the rendered width of the longest string:

  • Rough bound: text_width_px ≈ chars × font_size × 0.6 for proportional fonts at common weights.
  • If the container is narrower than the longest text, choose one of:
    • Widen the container (preferred when there's space).
    • Truncate to a short label (e.g. a count, a code, or an abbreviation) and put the full text outside (legend, footnote table, sidebar).
    • Wrap to multiple <tspan> lines when truncation loses meaning.
  • SVG doesn't clip text by default — never assume CSS overflow rules apply.

Default to counts + short labels inside the chart, full names in a legend below. For dense categorical charts this is almost always the right answer.

Rule 2 — Text-over-background needs a contrast contract

If a <text> element renders on top of an arbitrary background, declare which class of background it's designed for:

  • Light-bg text (fill: #1a1a1a or similar): legible on whites, light grays, light pastels.
  • Dark-bg text (fill: #ffffff or similar): legible on saturated mid-to-dark colors only.
  • High-contrast halo (paint-order="stroke fill" + stroke-width="3" + stroke="#fff"): legible on anything; use when the background is unpredictable or varies.

Never apply one fill class across multiple background colors without verifying contrast for each. WCAG large-text minimum is 3:1; for text-in-data-viz aim for 4.5:1 to leave margin.

When colors are picked by category, group them into a "dark fills" palette (use white text) and a "light fills" palette (use dark text). Don't mix.

Rule 3 — Backgrounds for text on uncertain surfaces

When text is placed near or over a colored region whose color depends on data, give the text its own background:

  • A <rect> behind the text, sized to its bounding box (compute as chars × font_size × 0.6 + padding), with fill="#fff" and a small rx for softness.
  • Or a halo via paint-order="stroke fill" with a white stroke.

For category legends, axis labels, and chart titles — anything that sits in the chart's "frame" rather than inside a data shape — always give them a clean background or place them in the margin.

Rule 4 — Reserve margin for rotated labels

Rotated text (typically column headers at -30° to -45°) extends beyond its anchor in both x and y. Rule of thumb: an N-character rotated header at θ degrees and font-size F needs:

  • horizontal reach: cos(θ) × chars × F × 0.6
  • vertical reach: sin(θ) × chars × F × 0.6

Plus the anchor offset. Always add 20% padding. If the rotated headers extend into the chart body, increase the chart's top margin until they don't.

Rule 5 — Default chart pattern: sparse cells, full legend below

For categorical density / heatmap / matrix charts:

[ Title                                                     ]
[ Rotated category headers (with margin)                    ]
[ Row label │ count │ count │   .  │ count │ count │   .  ]   (cells: just the number, color-coded)
[ Row label │   .  │ count │ count │   .  │ count │   .  ]
[ ...                                                       ]
[                                                           ]
[ Legend / details table:                                   ]
[ Row × Category → action_a, action_b, action_c             ]
[ Row × Category → action_d                                 ]
[ ...                                                       ]

Counts inside cells; names in the table below. This pattern composes well across renderers and prevents the overflow trap entirely.

Rule 6 — Stick to a small palette with declared roles

Categorical palettes should be picked once, with their role declared:

  • "Dark fills" (use white text): #1f77b4, #d62728, #9467bd, #8c564b
  • "Light fills" (use dark text): #aec7e8, #ffbb78, #98df8a

Avoid mid-tones (#ff7f0e, #2ca02c, #e377c2) unless the text color is matched per-color. If the chart needs more than 6 categories, that's usually a sign the categorisation is too fine — group first.

Rule 7 — Sanity check the rendered output

Before saving, mentally render at the target display width (typically 600750px effective in Markdown), and ask:

  • Can I read every label?
  • Does any text overlap another shape?
  • Is there a category whose color makes its text invisible?
  • Are empty cells distinguishable from filled ones at a glance?

If any answer is "no", iterate before checking in.