Visual Media Integration: Images, Charts & Cover Images skill

- Cover Images & OG Images

by AgriciDaniel·MIT license·★ 2,219 Stars on the repo·GitHub ↗

Use now

Files of Visual Media Integration: Images, Charts & Cover Images

AgriciDaniel/main1 file
visual-media.md
Show the full text432 lines

Visual Media Integration: Images, Charts & Cover Images

Contents

Cover Images & OG Images

Every blog post should have a cover image for social sharing and blog listings.

Option 1: Photo Cover (Pixabay/Unsplash/Pexels)

Search for a wide, high-quality image relevant to the topic through official APIs when keys are available, or through Openverse for CC assets. Download the chosen asset locally, store attribution/license metadata, and do not hotlink raw CDN URLs.

Sizing requirements:

Use Case Dimensions Aspect Ratio
Blog hero/cover 1200x630 or 1920x1080 1.91:1 or 16:9
Open Graph (OG) 1200x630 1.91:1 (required)
Twitter card 1200x628 ~1.91:1

Resize/crop locally to the target dimensions and keep hero-credit.txt or equivalent attribution next to the downloaded asset.

Option 2: Generated Chart Cover (via blog-chart)

For branded or data-driven covers, generate via blog-chart:

  • Text-on-gradient with title and key statistic
  • Dark-mode compatible (use currentColor where possible)
  • Include blog name/author subtle branding
  • ViewBox: 0 0 1200 630 for OG compatibility
  • Render the final social image to PNG or WebP at 1200x630. Do not use raw SVG as og:image; many social parsers do not reliably render SVG previews.
Option 3: AI-Generated Cover (via blog-image)

For custom, topic-specific covers when stock photos don't match:

  1. Requires nanobanana-mcp configured (see /blog image setup)
  2. Uses 6-component Reasoning Brief for optimized Gemini prompts
  3. Supports 14 aspect ratios (16:9 for hero, 1.91:1 for OG)
  4. Use current Gemini image models: gemini-3.1-flash-image, gemini-3.1-flash-lite-image, or gemini-3-pro-image
  5. Post-processing: auto-resize to 1200x630, convert to WebP/AVIF

Best for: Abstract topics, branded imagery, niche subjects with poor stock results.

Frontmatter Fields
---
title: "..."
description: "..."
coverImage: "/images/blog/topic-cover.jpg"
coverImageAlt: "Descriptive sentence about the cover image"
ogImage: "/images/blog/topic-og.jpg"  # Same as cover or custom OG
date: "YYYY-MM-DD"
---
  • coverImage: displayed as hero at the top of the post
  • ogImage: used for social sharing previews (Open Graph / Twitter Card)
  • If only one image, use the same URL for both fields
  • Alt text is required for the cover image
When to Use Each Option
Scenario Recommendation
General topic Photo cover from Pixabay/Unsplash/Pexels
Data-heavy article Generated SVG with key stat highlight
Brand-focused Generated SVG with brand colors
Abstract/niche topic AI-generated via blog-image (Gemini)
Tutorial/how-to Screenshot or relevant photo

Image Sourcing

Pixabay (Preferred)
  • License: Pixabay Content License - free for commercial use, no attribution required
  • URL: https://pixabay.com
  • Hotlinking: Allowed via CDN URLs

Finding images:

  1. WebSearch: site:pixabay.com [topic keywords]
  2. Visit the image page to get the direct CDN URL
  3. Direct URL pattern: https://cdn.pixabay.com/photo/YYYY/MM/DD/HH/MM/filename.jpg
  4. Verify: curl -sI "<url>" | head -1 - must return HTTP 200

Sizing: Append query params for optimization:

  • Blog hero: original size (typically 1920px wide)
  • Inline images: use as-is (most are 1280px+)
Unsplash (Alternative)
  • License: Unsplash License - free for commercial use, no attribution required
  • URL: https://unsplash.com
  • Hotlinking: Required - must use their CDN

Finding images:

  1. WebSearch: site:unsplash.com [topic keywords]
  2. Extract photo ID from URL (e.g., photo-1234567890123-abcdef)
  3. Build direct URL: https://images.unsplash.com/photo-<id>?w=1200&h=630&fit=crop&q=80
  4. Verify: curl -sI "<url>" | head -1 - must return HTTP 200
Pexels (Fallback)
  • License: Pexels License - free for commercial use, no attribution required
  • URL: https://pexels.com
  • Finding: WebSearch site:pexels.com [topic keywords]
Image Usage Rules
Rule Requirement
Alt text Required on ALL images - full descriptive sentence
Placement After H2 headings, before body text
Distribution Spread evenly - never cluster images
Count Intent-based density within the page-weight budget
Relevance Must relate to adjacent content
Format AVIF preferred, WebP fallback, JPEG last resort
Image Density by Content Type

Use one density model: add visuals where they clarify, prove, or summarize the adjacent section, while keeping total image payload under the page budget.

Content Type Typical Density Example (2,000-word post)
Standard article 1 visual per 400-600 words 3-5 visuals
How-to or tutorial 1 visual per major step 5-8 visuals
Listicle or product roundup 1 visual per item only when useful 5-10 visuals
Case study or data post charts/screenshots for proof points 4-7 visuals

Avoid mechanical image-every-75-100-word targets. Use optimized formats (AVIF/WebP) and responsive sizes; if the post exceeds the page-weight budget, reduce decorative media before cutting proof screenshots or charts.

SVG Impact on Engagement

D.C. Thomson case study results after replacing raster images with contextual SVGs:

  • Session duration doubled
  • 317% increase in read-to-completion rate
  • SVGs are resolution-independent, lightweight, and dark-mode compatible
Alt Text Guidelines
  • Full descriptive sentence including topic keywords naturally
  • Describe what the image shows AND its relevance to the content
  • 10-125 characters
  • No keyword stuffing - natural language only

Good: Marketing team analyzing AI search traffic data on a dashboard showing citation metrics Bad: SEO AI marketing blog optimization image

AI Systems and Images: AI crawlers read alt text and captions, NOT the images themselves. Write context-rich alt text that conveys the data or insight the image represents. For charts, include the key data point in the alt text. For screenshots, describe what the screenshot demonstrates.

Embedding Images

Standard Markdown:

![Descriptive alt text sentence](https://cdn.pixabay.com/photo/.../image.jpg)

MDX (Next.js):

![Descriptive alt text sentence](https://cdn.pixabay.com/photo/.../image.jpg)

For Next.js projects, verify next.config.ts includes the image domain:

images: {
  remotePatterns: [
    { protocol: 'https', hostname: 'cdn.pixabay.com' },
    { protocol: 'https', hostname: 'images.unsplash.com' },
    { protocol: 'https', hostname: 'images.pexels.com' },
  ],
}

HTML:

<figure>
  <img src="https://cdn.pixabay.com/photo/.../image.jpg"
       alt="Descriptive alt text sentence"
       width="1200" height="630" loading="lazy">
  <figcaption>Photo via Pixabay</figcaption>
</figure>

Image Format Optimization

AVIF as Primary Format

AVIF is the recommended image format for 2026:

  • ~50% smaller than JPEG at equivalent quality
  • ~20-30% smaller than WebP
  • 93.8% global browser support (caniuse, Jan 2026)
  • Supports HDR, wide color gamut, and transparency
<picture> Element with Progressive Fallback

Always use the <picture> element for format negotiation:

<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" alt="Descriptive alt text" width="1200" height="630" loading="lazy">
</picture>

This pattern serves AVIF to supporting browsers, falls back to WebP, then JPEG.

LCP Image Rules

NEVER use loading="lazy" on hero/LCP (Largest Contentful Paint) images. Lazy loading the LCP image delays the largest element on the page and directly harms Core Web Vitals scores.

For hero/above-the-fold images:

<img src="hero.avif" alt="..." width="1200" height="630"
     fetchpriority="high" decoding="async">

For below-the-fold images:

<img src="image.avif" alt="..." width="800" height="450"
     loading="lazy" decoding="async">
Dark Mode Image Support

Use <picture> with prefers-color-scheme media query for theme-aware images:

<picture>
  <source srcset="chart-dark.avif" media="(prefers-color-scheme: dark)" type="image/avif">
  <source srcset="chart-dark.webp" media="(prefers-color-scheme: dark)" type="image/webp">
  <source srcset="chart-light.avif" type="image/avif">
  <source srcset="chart-light.webp" type="image/webp">
  <img src="chart-light.jpg" alt="Descriptive alt text" width="800" height="450">
</picture>

CSS variable pattern for inline SVG dark mode:

:root {
  --chart-bg: #ffffff;
  --chart-text: #111827;
  --chart-grid: rgba(0, 0, 0, 0.08);
}

@media (prefers-color-scheme: dark) {
  :root {
    --chart-bg: transparent;
    --chart-text: #f3f4f6;
    --chart-grid: rgba(255, 255, 255, 0.08);
  }
}

SVG Chart Integration (Built-In)

Charts are generated by the blog-chart sub-skill. The writer identifies chart-worthy data during the writing process and delegates chart generation internally.

Chart Type Selection Guide
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

Diversity is mandatory - never use the same chart type twice in one post. Target 2-4 charts per 2,000-word post.

Dark-Mode Compatible Styling

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="currentColor" opacity="0.45"
Source text:        fill="currentColor" opacity="0.35"
Label text:         fill="currentColor" opacity="0.8"
Color Palette (works on dark and light)
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 colored elements: fill="white" with fontWeight="800".

Standard SVG Shell
<svg
  viewBox="0 0 560 380"
  style="max-width: 100%; height: auto; font-family: 'Inter', system-ui, sans-serif"
  role="img"
  aria-label="Chart description with key data point"
>
  <title>Chart Title</title>
  <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="currentColor" opacity="0.35">
    Source: Source Name (Year)
  </text>
</svg>
JSX/MDX Shell (camelCase attributes)
<svg
  viewBox="0 0 560 380"
  style={{maxWidth: '100%', height: 'auto', fontFamily: "'Inter', system-ui, sans-serif"}}
  role="img"
  aria-label="Chart description"
>
  <title>Chart Title</title>
  <desc>Description for screen readers</desc>

  {/* Chart content */}

  <text x="280" y="372" textAnchor="middle" fontSize="10" fill="currentColor" opacity="0.35">
    Source: Source Name (Year)
  </text>
</svg>
JSX Attribute Conversion (Required for MDX)
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={{...}}
Embedding Charts

Standard HTML:

<figure>
  <svg viewBox="0 0 560 380" ...>...</svg>
  <figcaption>Source: Source Name, Year</figcaption>
</figure>

MDX:

<figure className="chart-container" style={{margin: '2.5rem 0', textAlign: 'center', padding: '1.5rem', borderRadius: '12px'}}>
  <svg viewBox="0 0 560 380" ...>...</svg>
</figure>
Invoking blog-chart

When generating charts, pass to the blog-chart sub-skill:

  1. Chart type (ensure diversity - never repeat within a post)
  2. Title for the chart
  3. Exact data values with sources
  4. Source attribution (name and year)
  5. Platform format: html or mdx

The sub-skill returns complete SVG wrapped in a <figure>. Verify before embedding:

  1. currentColor usage (no hardcoded text colors)
  2. No white/light backgrounds
  3. If MDX: camelCase attributes
  4. Source attribution present
Common Pitfalls
Mistake Impact Fix
fill="#111827" on text Invisible on dark mode Use fill="currentColor"
rect fill="white" background Bright flash on dark mode Remove or use transparent
stroke-width in MDX Compilation error Use strokeWidth
class in MDX Compilation error Use className
Same chart type twice Visual monotony Enforce chart diversity
No role="img" Accessibility failure Always include
No source attribution Trust issue Always cite data source

YouTube Video Embeds

YouTube videos are part of the visual media mix alongside images, charts, and AI-generated images. Vendor studies report a strong correlation with AI visibility, but treat that as directional and use videos only when relevant.

See video-embeds.md for:

  • Embed patterns (srcdoc lazy loading for MDX, HTML, Markdown, Hugo)
  • Video quality criteria and scoring (min 50/100)
  • Placement strategy (2-3 per post, 500+ words apart)
  • VideoObject JSON-LD schema template
  • Noscript fallback for AI crawlers
1# Visual Media Integration: Images, Charts & Cover Images
2 
3## Contents
4 
5- [Cover Images & OG Images](#cover-images--og-images)
6- [Image Sourcing](#image-sourcing)
7- [Image Format Optimization](#image-format-optimization)
8- [SVG Chart Integration (Built-In)](#svg-chart-integration-built-in)
9- [YouTube Video Embeds](#youtube-video-embeds)
10 
11## Cover Images & OG Images
12 
13Every blog post should have a cover image for social sharing and blog listings.
14 
15### Option 1: Photo Cover (Pixabay/Unsplash/Pexels)
16 
17Search for a wide, high-quality image relevant to the topic through official
18APIs when keys are available, or through Openverse for CC assets. Download the
19chosen asset locally, store attribution/license metadata, and do not hotlink raw
20CDN URLs.
21 
22**Sizing requirements:**
23| Use Case | Dimensions | Aspect Ratio |
24|----------|-----------|--------------|
25| Blog hero/cover | 1200x630 or 1920x1080 | 1.91:1 or 16:9 |
26| Open Graph (OG) | 1200x630 | 1.91:1 (required) |
27| Twitter card | 1200x628 | ~1.91:1 |
28 
29Resize/crop locally to the target dimensions and keep `hero-credit.txt` or
30equivalent attribution next to the downloaded asset.
31 
32### Option 2: Generated Chart Cover (via blog-chart)
33 
34For branded or data-driven covers, generate via `blog-chart`:
35- Text-on-gradient with title and key statistic
36- Dark-mode compatible (use `currentColor` where possible)
37- Include blog name/author subtle branding
38- ViewBox: `0 0 1200 630` for OG compatibility
39- Render the final social image to PNG or WebP at 1200x630. Do not use raw SVG
40 as `og:image`; many social parsers do not reliably render SVG previews.
41 
42### Option 3: AI-Generated Cover (via blog-image)
43 
44For custom, topic-specific covers when stock photos don't match:
451. Requires nanobanana-mcp configured (see `/blog image setup`)
462. Uses 6-component Reasoning Brief for optimized Gemini prompts
473. Supports 14 aspect ratios (16:9 for hero, 1.91:1 for OG)
484. Use current Gemini image models: `gemini-3.1-flash-image`,
49 `gemini-3.1-flash-lite-image`, or `gemini-3-pro-image`
505. Post-processing: auto-resize to 1200x630, convert to WebP/AVIF
51 
52Best for: Abstract topics, branded imagery, niche subjects with poor stock results.
53 
54### Frontmatter Fields
55 
56```yaml
57---
58title: "..."
59description: "..."
60coverImage: "/images/blog/topic-cover.jpg"
61coverImageAlt: "Descriptive sentence about the cover image"
62ogImage: "/images/blog/topic-og.jpg" # Same as cover or custom OG
63date: "YYYY-MM-DD"
64---
65```
66 
67- `coverImage`: displayed as hero at the top of the post
68- `ogImage`: used for social sharing previews (Open Graph / Twitter Card)
69- If only one image, use the same URL for both fields
70- Alt text is required for the cover image
71 
72### When to Use Each Option
73 
74| Scenario | Recommendation |
75|----------|---------------|
76| General topic | Photo cover from Pixabay/Unsplash/Pexels |
77| Data-heavy article | Generated SVG with key stat highlight |
78| Brand-focused | Generated SVG with brand colors |
79| Abstract/niche topic | AI-generated via `blog-image` (Gemini) |
80| Tutorial/how-to | Screenshot or relevant photo |
81 
82---
83 
84## Image Sourcing
85 
86### Pixabay (Preferred)
87- **License**: Pixabay Content License - free for commercial use, no attribution required
88- **URL**: https://pixabay.com
89- **Hotlinking**: Allowed via CDN URLs
90 
91**Finding images:**
921. WebSearch: `site:pixabay.com [topic keywords]`
932. Visit the image page to get the direct CDN URL
943. Direct URL pattern: `https://cdn.pixabay.com/photo/YYYY/MM/DD/HH/MM/filename.jpg`
954. Verify: `curl -sI "<url>" | head -1` - must return HTTP 200
96 
97**Sizing**: Append query params for optimization:
98- Blog hero: original size (typically 1920px wide)
99- Inline images: use as-is (most are 1280px+)
100 
101### Unsplash (Alternative)
102- **License**: Unsplash License - free for commercial use, no attribution required
103- **URL**: https://unsplash.com
104- **Hotlinking**: Required - must use their CDN
105 
106**Finding images:**
1071. WebSearch: `site:unsplash.com [topic keywords]`
1082. Extract photo ID from URL (e.g., `photo-1234567890123-abcdef`)
1093. Build direct URL: `https://images.unsplash.com/photo-<id>?w=1200&h=630&fit=crop&q=80`
1104. Verify: `curl -sI "<url>" | head -1` - must return HTTP 200
111 
112### Pexels (Fallback)
113- **License**: Pexels License - free for commercial use, no attribution required
114- **URL**: https://pexels.com
115- **Finding**: WebSearch `site:pexels.com [topic keywords]`
116 
117### Image Usage Rules
118 
119| Rule | Requirement |
120|------|-------------|
121| Alt text | Required on ALL images - full descriptive sentence |
122| Placement | After H2 headings, before body text |
123| Distribution | Spread evenly - never cluster images |
124| Count | Intent-based density within the page-weight budget |
125| Relevance | Must relate to adjacent content |
126| Format | AVIF preferred, WebP fallback, JPEG last resort |
127 
128### Image Density by Content Type
129 
130Use one density model: add visuals where they clarify, prove, or summarize the
131adjacent section, while keeping total image payload under the page budget.
132 
133| Content Type | Typical Density | Example (2,000-word post) |
134|-------------|-----------------|---------------------------|
135| Standard article | 1 visual per 400-600 words | 3-5 visuals |
136| How-to or tutorial | 1 visual per major step | 5-8 visuals |
137| Listicle or product roundup | 1 visual per item only when useful | 5-10 visuals |
138| Case study or data post | charts/screenshots for proof points | 4-7 visuals |
139 
140Avoid mechanical image-every-75-100-word targets. Use optimized formats
141(AVIF/WebP) and responsive sizes; if the post exceeds the page-weight budget,
142reduce decorative media before cutting proof screenshots or charts.
143 
144### SVG Impact on Engagement
145 
146D.C. Thomson case study results after replacing raster images with contextual SVGs:
147- Session duration doubled
148- 317% increase in read-to-completion rate
149- SVGs are resolution-independent, lightweight, and dark-mode compatible
150 
151### Alt Text Guidelines
152- Full descriptive sentence including topic keywords naturally
153- Describe what the image shows AND its relevance to the content
154- 10-125 characters
155- No keyword stuffing - natural language only
156 
157Good: `Marketing team analyzing AI search traffic data on a dashboard showing citation metrics`
158Bad: `SEO AI marketing blog optimization image`
159 
160**AI Systems and Images**: AI crawlers read alt text and captions, NOT the images
161themselves. Write context-rich alt text that conveys the data or insight the image
162represents. For charts, include the key data point in the alt text. For screenshots,
163describe what the screenshot demonstrates.
164 
165### Embedding Images
166 
167**Standard Markdown:**
168```markdown
169![Descriptive alt text sentence](https://cdn.pixabay.com/photo/.../image.jpg)
170```
171 
172**MDX (Next.js):**
173```mdx
174![Descriptive alt text sentence](https://cdn.pixabay.com/photo/.../image.jpg)
175```
176 
177For Next.js projects, verify `next.config.ts` includes the image domain:
178```typescript
179images: {
180 remotePatterns: [
181 { protocol: 'https', hostname: 'cdn.pixabay.com' },
182 { protocol: 'https', hostname: 'images.unsplash.com' },
183 { protocol: 'https', hostname: 'images.pexels.com' },
184 ],
185}
186```
187 
188**HTML:**
189```html
190<figure>
191 <img src="https://cdn.pixabay.com/photo/.../image.jpg"
192 alt="Descriptive alt text sentence"
193 width="1200" height="630" loading="lazy">
194 <figcaption>Photo via Pixabay</figcaption>
195</figure>
196```
197 
198---
199 
200## Image Format Optimization
201 
202### AVIF as Primary Format
203 
204AVIF is the recommended image format for 2026:
205- ~50% smaller than JPEG at equivalent quality
206- ~20-30% smaller than WebP
207- 93.8% global browser support (caniuse, Jan 2026)
208- Supports HDR, wide color gamut, and transparency
209 
210### `<picture>` Element with Progressive Fallback
211 
212Always use the `<picture>` element for format negotiation:
213 
214```html
215<picture>
216 <source srcset="image.avif" type="image/avif">
217 <source srcset="image.webp" type="image/webp">
218 <img src="image.jpg" alt="Descriptive alt text" width="1200" height="630" loading="lazy">
219</picture>
220```
221 
222This pattern serves AVIF to supporting browsers, falls back to WebP, then JPEG.
223 
224### LCP Image Rules
225 
226**NEVER** use `loading="lazy"` on hero/LCP (Largest Contentful Paint) images.
227Lazy loading the LCP image delays the largest element on the page and directly
228harms Core Web Vitals scores.
229 
230For hero/above-the-fold images:
231```html
232<img src="hero.avif" alt="..." width="1200" height="630"
233 fetchpriority="high" decoding="async">
234```
235 
236For below-the-fold images:
237```html
238<img src="image.avif" alt="..." width="800" height="450"
239 loading="lazy" decoding="async">
240```
241 
242### Dark Mode Image Support
243 
244Use `<picture>` with `prefers-color-scheme` media query for theme-aware images:
245 
246```html
247<picture>
248 <source srcset="chart-dark.avif" media="(prefers-color-scheme: dark)" type="image/avif">
249 <source srcset="chart-dark.webp" media="(prefers-color-scheme: dark)" type="image/webp">
250 <source srcset="chart-light.avif" type="image/avif">
251 <source srcset="chart-light.webp" type="image/webp">
252 <img src="chart-light.jpg" alt="Descriptive alt text" width="800" height="450">
253</picture>
254```
255 
256CSS variable pattern for inline SVG dark mode:
257```css
258:root {
259 --chart-bg: #ffffff;
260 --chart-text: #111827;
261 --chart-grid: rgba(0, 0, 0, 0.08);
262}
263 
264@media (prefers-color-scheme: dark) {
265 :root {
266 --chart-bg: transparent;
267 --chart-text: #f3f4f6;
268 --chart-grid: rgba(255, 255, 255, 0.08);
269 }
270}
271```
272 
273---
274 
275## SVG Chart Integration (Built-In)
276 
277Charts are generated by the `blog-chart` sub-skill. The writer identifies chart-worthy
278data during the writing process and delegates chart generation internally.
279 
280### Chart Type Selection Guide
281 
282| Data Pattern | Best Chart Type |
283|-------------|-----------------|
284| Before/after comparison | Grouped bar chart |
285| Ranked factors / correlations | Lollipop chart |
286| Parts of whole / market share | Donut chart |
287| Trend over time | Line chart |
288| Percentage improvement | Horizontal bar chart |
289| Distribution / range | Area chart |
290| Multi-dimensional scoring | Radar chart |
291 
292**Diversity is mandatory** - never use the same chart type twice in one post.
293Target 2-4 charts per 2,000-word post.
294 
295### Dark-Mode Compatible Styling
296 
297All charts must work on both dark and light backgrounds:
298 
299```
300Text elements: fill="currentColor"
301Grid lines: stroke="currentColor" opacity="0.08"
302Axis lines: stroke="currentColor" opacity="0.3"
303Background: transparent (no fill on root SVG)
304Subtitle text: fill="currentColor" opacity="0.45"
305Source text: fill="currentColor" opacity="0.35"
306Label text: fill="currentColor" opacity="0.8"
307```
308 
309### Color Palette (works on dark and light)
310 
311| Color | Hex | Use Case |
312|-------|-----|----------|
313| Orange | `#f97316` | Primary / highest value |
314| Sky Blue | `#38bdf8` | Secondary / comparison |
315| Purple | `#a78bfa` | Tertiary / special category |
316| Green | `#22c55e` | Quaternary / positive indicator |
317 
318For text inside colored elements: `fill="white"` with `fontWeight="800"`.
319 
320### Standard SVG Shell
321 
322```xml
323<svg
324 viewBox="0 0 560 380"
325 style="max-width: 100%; height: auto; font-family: 'Inter', system-ui, sans-serif"
326 role="img"
327 aria-label="Chart description with key data point"
328>
329 <title>Chart Title</title>
330 <desc>Description for screen readers with all key data points and source</desc>
331 
332 <!-- Chart content -->
333 
334 <text x="280" y="372" text-anchor="middle" font-size="10" fill="currentColor" opacity="0.35">
335 Source: Source Name (Year)
336 </text>
337</svg>
338```
339 
340### JSX/MDX Shell (camelCase attributes)
341 
342```jsx
343<svg
344 viewBox="0 0 560 380"
345 style={{maxWidth: '100%', height: 'auto', fontFamily: "'Inter', system-ui, sans-serif"}}
346 role="img"
347 aria-label="Chart description"
348>
349 <title>Chart Title</title>
350 <desc>Description for screen readers</desc>
351 
352 {/* Chart content */}
353 
354 <text x="280" y="372" textAnchor="middle" fontSize="10" fill="currentColor" opacity="0.35">
355 Source: Source Name (Year)
356 </text>
357</svg>
358```
359 
360### JSX Attribute Conversion (Required for MDX)
361 
362| HTML | JSX |
363|------|-----|
364| `stroke-width` | `strokeWidth` |
365| `stroke-dasharray` | `strokeDasharray` |
366| `stroke-linecap` | `strokeLinecap` |
367| `text-anchor` | `textAnchor` |
368| `font-size` | `fontSize` |
369| `font-weight` | `fontWeight` |
370| `font-family` | `fontFamily` |
371| `class` | `className` |
372| `style="..."` | `style={{...}}` |
373 
374### Embedding Charts
375 
376**Standard HTML:**
377```html
378<figure>
379 <svg viewBox="0 0 560 380" ...>...</svg>
380 <figcaption>Source: Source Name, Year</figcaption>
381</figure>
382```
383 
384**MDX:**
385```mdx
386<figure className="chart-container" style={{margin: '2.5rem 0', textAlign: 'center', padding: '1.5rem', borderRadius: '12px'}}>
387 <svg viewBox="0 0 560 380" ...>...</svg>
388</figure>
389```
390 
391### Invoking blog-chart
392 
393When generating charts, pass to the `blog-chart` sub-skill:
3941. **Chart type** (ensure diversity - never repeat within a post)
3952. **Title** for the chart
3963. **Exact data values** with sources
3974. **Source attribution** (name and year)
3985. **Platform format**: html or mdx
399 
400The sub-skill returns complete SVG wrapped in a `<figure>`. Verify before embedding:
4011. `currentColor` usage (no hardcoded text colors)
4022. No white/light backgrounds
4033. If MDX: camelCase attributes
4044. Source attribution present
405 
406### Common Pitfalls
407 
408| Mistake | Impact | Fix |
409|---------|--------|-----|
410| `fill="#111827"` on text | Invisible on dark mode | Use `fill="currentColor"` |
411| `rect fill="white"` background | Bright flash on dark mode | Remove or use transparent |
412| `stroke-width` in MDX | Compilation error | Use `strokeWidth` |
413| `class` in MDX | Compilation error | Use `className` |
414| Same chart type twice | Visual monotony | Enforce chart diversity |
415| No `role="img"` | Accessibility failure | Always include |
416| No source attribution | Trust issue | Always cite data source |
417 
418---
419 
420## YouTube Video Embeds
421 
422YouTube videos are part of the visual media mix alongside images, charts, and
423AI-generated images. Vendor studies report a strong correlation with AI
424visibility, but treat that as directional and use videos only when relevant.
425 
426See `video-embeds.md` for:
427- Embed patterns (srcdoc lazy loading for MDX, HTML, Markdown, Hugo)
428- Video quality criteria and scoring (min 50/100)
429- Placement strategy (2-3 per post, 500+ words apart)
430- VideoObject JSON-LD schema template
431- Noscript fallback for AI crawlers
432 

Discussion