SVG suggestions #4
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Improvements for the
markdown-embedded-svgskillNotes captured while reviewing concrete SVG output from agents in
v1-superset-analysis/. The current skill focuses on renderer compatibility (which Markdown renderers sanitize<svg>, where to put files, GitHub strips, Gitea allows little, Obsidian allows most). That's the right thing to cover and it stays correct. But the skill says nothing about legibility, and the output suffers for it. Below is what to add.Concrete failure modes observed
From
v1-superset-analysis/images/action-density-map.svg(the matrix of 32@actions by ViewSet × category):Text overflows its container. Each cell is 80×40 px and contains strings like
"fetch, fetch_fixed, copy, copy_mapping"or"columns, map_columns, columns_detail, default_mapping". At 11px font that's ~280px of text in an 80px box. The text bleeds into adjacent cells; the chart becomes a soup of overlapping labels.White text on light-mid backgrounds. The CSS class
.lbl { fill: #fff }is applied uniformly to text laid over fills#1f77b4(dark blue — OK),#ff7f0e(mid orange — poor contrast),#2ca02c(mid green — marginal),#9467bd(mid purple — OK),#e377c2(pink — bad). Same color choice for all backgrounds means the worst pairing is illegible.No background or halo behind text that sits on a colored field. When a label does land on a fill that doesn't match its text color (e.g. an empty
#f4f4f4cell with text overflowing from a neighbor), there's nothing to rescue it.Rotated column headers at
-30°overlap downward into the matrix without measuring how much vertical margin they actually need.No empty-state markers. Empty cells are flat light gray — fine — but every other cell is busy, which makes empties feel like missing data rather than deliberate "no actions in this category."
What the skill should add — concrete rules
Rule 1 — Estimate max-text-width before sizing a container
For any text that lives inside a bounded shape (cell, box, button), the SVG author must estimate the rendered width of the longest string:
text_width_px ≈ chars × font_size × 0.6for proportional fonts at common weights.<tspan>lines when truncation loses meaning.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:fill: #1a1a1aor similar): legible on whites, light grays, light pastels.fill: #ffffffor similar): legible on saturated mid-to-dark colors only.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:
<rect>behind the text, sized to its bounding box (compute aschars × font_size × 0.6 + padding), withfill="#fff"and a smallrxfor softness.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:cos(θ) × chars × F × 0.6sin(θ) × chars × F × 0.6Plus 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:
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:
#1f77b4,#d62728,#9467bd,#8c564b— use white text.#aec7e8,#ffbb78,#98df8a— use dark text.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 renderer's display width (typically 600-900px effective in Markdown), and ask:
If any answer is "no", iterate before checking in.
Suggested addition to the skill body
Add a section titled "Layout and legibility" between the renderer-compatibility section and the file-placement section. Bullet the seven rules above. Add one good and one bad worked example side by side — the bad one matching the action-density failure mode (text in narrow cells), the good one matching the sparse-cells-plus-legend pattern.
If the skill bundles assets, include a
palettes.svgorpalettes.mdthat names two or three pre-baked palettes (categorical-6-dark, categorical-6-light, sequential, diverging) with declared text-color pairings so an agent doesn't have to derive contrast from scratch each time.fixed