Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate dark-mode-compatible inline SVG data visualization charts for blog posts. Supports horizontal bar, grouped bar, donut, line, lollipop, area, and radar charts with automatic platform detection (HTML vs JSX/MDX). Enforces chart type diversity, accessible markup (role=img, aria-labelledby), source attribution, and transparent backgrounds. Use when user says "blog chart", "generate chart", "data visualization", "svg chart", "blog graph", or "visualize data".
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 60% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 21% | 0% |
| case-09 | ✗→✓ | ▲ Improved | -9% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 178% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 32% | 0% |
Generates dark-mode-compatible inline SVG charts for blog posts. Invoked internally by blog-write and blog-rewrite when chart-worthy data is identified. Not a standalone user-facing command.
Styling source of truth: skills/blog/references/visual-media.md
For supported chart types, prefer the deterministic CLI:
bashpython3 skills/blog-chart/scripts/generate_chart_svg.py --input chart.json --output chart.html --json
The writer or researcher passes a chart request:
Chart Request:
- Type: horizontal bar
- Title: "AI Citation Sources by Platform"
- Data: ChatGPT 43.8%, Perplexity 6.6%, Google AI Overviews 2.2%, Reddit 7.15%
- Source: [Verified source], [publication date]
- Platform: mdx (or html)Select based on the data pattern. Prefer chart type diversity, but repeat a type when comparability or reader comprehension clearly benefits.
| Data Pattern | Best Chart Type | |-------------|-----------------| | Before/after comparison | Grouped bar chart | | Ranked factors / correlations | Lollipop chart | | Parts of whole / market share | Donut chart | | Trend over time | Line chart | | Percentage improvement | Horizontal bar chart | | Distribution / range | Area chart | | Multi-dimensional scoring | Radar chart |
All charts must work on both dark and light backgrounds:
Text elements: fill="currentColor"
Grid lines: stroke="currentColor" opacity="0.08"
Axis lines: stroke="currentColor" opacity="0.3"
Background: transparent (no fill on root SVG)
Subtitle text: fill="var(--chart-muted, currentColor)"
Source text: fill="var(--chart-muted, currentColor)"
Label text: fill="currentColor" opacity="0.8"Set --chart-muted to an accessible text token in the host theme. If no token exists, use #4b5563 on light backgrounds and #d1d5db on dark backgrounds. Do not rely on low-opacity source or subtitle text for visible attribution.
| Color | Hex | Use Case | |-------|-----|----------| | Orange | #f97316 | Primary / highest value | | Sky Blue | #38bdf8 | Secondary / comparison | | Purple | #a78bfa | Tertiary / special category | | Green | #22c55e | Quaternary / positive indicator |
For text inside approved colored elements: use fill="#111827" with fontWeight="800". Only use white text after checking the contrast ratio is at least 4.5:1 against that specific fill color.
Do not rely on color alone. Add direct labels, patterns, line dashes, marker shapes, or legend text so colorblind readers can distinguish series.
xml<svg viewBox="0 0 560 380" style="max-width: 100%; height: auto; font-family: 'Inter', system-ui, sans-serif" role="img" aria-labelledby="chart-title chart-desc" > <title id="chart-title">Chart Title</title> <desc id="chart-desc">Description for screen readers with all key data points and source</desc> <!-- Chart content --> <text x="280" y="372" text-anchor="middle" font-size="10" fill="var(--chart-muted, currentColor)"> Source: Source Name (Year) </text> </svg>
jsx<svg viewBox="0 0 560 380" style={{maxWidth: '100%', height: 'auto', fontFamily: "'Inter', system-ui, sans-serif"}} role="img" aria-labelledby="chart-title chart-desc" > <title id="chart-title">Chart Title</title> <desc id="chart-desc">Description for screen readers</desc> {/* Chart content */} <text x="280" y="372" textAnchor="middle" fontSize="10" fill="var(--chart-muted, currentColor)"> Source: Source Name (Year) </text> </svg>
| HTML | JSX | |------|-----| | stroke-width | strokeWidth | | stroke-dasharray | strokeDasharray | | stroke-linecap | strokeLinecap | | text-anchor | textAnchor | | font-size | fontSize | | font-weight | fontWeight | | font-family | fontFamily | | class | className | | style="..." | style={{...}} |
Best for: percentage improvements, single-metric comparisons.
chartHeight / dataCount - gap (gap=8)(value / maxValue) * chartWidthy = chartY + index * (barHeight + gap)Best for: before/after, A vs B comparisons.
Best for: parts of whole, market share.
<path d="M... A... L... A... Z" fill="color" />Best for: trends over time.
stroke="currentColor" opacity="0.08"<circle cx=... cy=... r="4" fill="color" /><polyline points="..." fill="none" stroke="color" strokeWidth="2" />opacity="0.1"Best for: ranked factors, correlations.
stroke="currentColor" opacity="0.15" strokeWidth="1"r="6" with fill colorBest for: distribution, cumulative data.
<path d="M... L... L... Z" fill="color" opacity="0.15" />stroke="color" strokeWidth="2" fill="none"Best for: multi-dimensional scoring (5-7 axes).
fill="color" opacity="0.2" stroke="color"<tspan> lines.label in <desc> or adjacent prose.
max-width: 100%; height: autostyle, or choose a justified wider viewBox for dense labels.
Wrap every chart in a <figure> element:
HTML:
html<figure> <svg viewBox="0 0 560 380" style="max-width: 100%; height: auto; font-family: 'Inter', system-ui, sans-serif" role="img" aria-labelledby="chart-title chart-desc"> <title id="chart-title">[Chart Title]</title> <desc id="chart-desc">[Full description with data points for screen readers]</desc> <!-- chart content --> <text x="280" y="372" text-anchor="middle" font-size="10" fill="var(--chart-muted, currentColor)"> Source: [Source Name] ([Year]) </text> </svg> <figcaption>Source: <a href="[Source URL]">[Source Name]</a>, [publication date].</figcaption> </figure>
MDX:
mdx<figure className="chart-container" style={{margin: '2.5rem 0', textAlign: 'center', padding: '1.5rem', borderRadius: '12px'}}> <svg viewBox="0 0 560 380" style={{maxWidth: '100%', height: 'auto', fontFamily: "'Inter', system-ui, sans-serif"}} role="img" aria-labelledby="chart-title chart-desc"> <title id="chart-title">[Chart Title]</title> <desc id="chart-desc">[Full description]</desc> {/* chart content with camelCase attributes */} <text x="280" y="372" textAnchor="middle" fontSize="10" fill="var(--chart-muted, currentColor)"> Source: [Source Name] ([Year]) </text> </svg> <figcaption>Source: <a href="[Source URL]">[Source Name]</a>, [publication date].</figcaption> </figure>
<figcaption> presentrole="img" and aria-labelledby present on <svg><title id> and <desc id> present inside <svg>0 0 560 380 (standard) or justified alternativeOther measured skills in the registry, with their headline benchmark lift.