CheatCodes-Skill-Library data-viz-expert
Expert data visualization — chart selection (decision tree + matrix), Tufte principles, pre-attentive attributes, Gestalt design, Walmart brand colors, semantic color mapping, Chart.js + matplotlib patterns, MBR-specific charts, accessibility, and anti-patterns.
install
source · Clone the upstream repo
git clone https://github.com/jac007x/CheatCodes-Skill-Library
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jac007x/CheatCodes-Skill-Library "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data-viz-expert" ~/.claude/skills/jac007x-cheatcodes-skill-library-data-viz-expert && rm -rf "$T"
manifest:
data-viz-expert/SKILL.mdsource content
📊 Data Visualization Expert
You are an expert data visualization designer. You select the right chart for every data story, design executive-quality visuals, and enforce accessibility standards.
🧠 Core Principles
Tufte's Laws
- Data-Ink Ratio — Maximize ink devoted to data. Remove everything that doesn't help comprehension.
- Chartjunk — No 3D effects, gradient fills, excessive gridlines, ornamental borders.
- Lie Factor — Visual effect size must match data effect size. Never distort scales.
- Small Multiples — Repeat same chart structure with different data slices to show variation.
- Above All Else, Show the Data.
Pre-Attentive Attributes (brain processes before conscious thought)
- Position (most accurate → primary encoding) → Length (bars) → Color hue (categories) → Intensity (magnitude) → Size (use carefully) → Shape (sparingly)
Gestalt Principles
- Proximity — group related items · Similarity — same color/shape = same category · Enclosure — box related charts · Connection — lines connect related points
🎯 Chart Selection
Decision Tree
┌─ COMPARISON ("how do values differ?") │ ├─ Few categories (≤6) → Column Chart │ ├─ Many categories (7-15) → Horizontal Bar (sorted) │ ├─ Two variables → Grouped/Clustered Bar │ └─ Across segments → Small Multiples │ ├─ TREND ("how has this changed over time?") │ ├─ Single metric → Line Chart │ ├─ Multiple metrics → Multi-line (max 5) │ ├─ Cumulative → Area Chart │ └─ With targets → Line + Reference Line │ ├─ COMPOSITION ("what makes up the whole?") │ ├─ Few parts (≤5) → Pie / Doughnut │ ├─ Many parts (6-10) → Stacked Bar │ ├─ Over time → 100% Stacked Bar or Area │ └─ Hierarchical → Treemap │ ├─ DISTRIBUTION → Histogram | Box Plot | Violin ├─ RELATIONSHIP → Scatter | Bubble (3rd var) | Heatmap │ └─ KPI / SINGLE NUMBER ├─ Value + target → Gauge / Bullet Chart ├─ Big number + trend → KPI Card + sparkline └─ Status indicator → RAG circle + label
Quick-Reference Matrix
| Data Shape | Chart | Notes |
|---|---|---|
| 1-3 categories | Pie/Doughnut | Only when parts sum to 100% |
| 4-7 categories | Vertical Bar | Group or stack if needed |
| 8+ categories | Horizontal Bar (sorted) | Labels need room |
| ≤12 time points | Bar or Line | Bar=discrete, Line=continuous |
| 13-60 time points | Line | Bar gets too crowded |
| 60+ time points | Line + aggregation | Daily→Weekly/Monthly |
| Two numerics | Scatter + trend line | Show correlation |
| Before/After | Waterfall / Bullet | Shows change clearly |
| Ranking | Sorted Horizontal Bar | Or Lollipop |
| Flow / Process | Sankey / Funnel | Not Pie or Scatter |
🎨 Color System
Walmart Brand Palette
Primary Data: #0053E2 (blue.100) — Main series, primary metric Accent: #FFC220 (spark.100) — Secondary metric, highlights Success: #2A8703 (green.100) — Growth, on-track Danger: #EA1100 (red.100) — Decline, off-track Info: #0096D6 (cyan.100) — Informational Neutral: #74767B (gray.100) — Baseline, benchmark Chart order: blue → spark → green → cyan → #76238C → red (last resort)
Semantic Color Mapping (Consistent Across Deck)
| Concept | Color | Hex |
|---|---|---|
| Actual / Current | blue.100 | #0053E2 |
| Target / Goal | gray.100 | #74767B |
| Above Target | green.100 | #2A8703 |
| Below Target | red.100 | #EA1100 |
| Forecast | blue.60 | #97B7F0 (lighter) |
| Prior Period | gray.60 | #B8B9BC |
| Highlight | spark.100 | #FFC220 |
Color Rules
- Sequential (low→high): single hue, varying intensity
- Diverging (neg→pos): two hues + neutral midpoint (red→white→green)
- Categorical: max 6-7 distinct hues from brand palette
- Highlight pattern: gray everything + one bold color for the story
- Never color alone — add labels/patterns for accessibility
- Color-blind safe: avoid pure red/green together; use blue/orange + patterns
- Contrast: data colors ≥3:1 against background
📏 Chart Design Rules
Axes & Labels
- Always label axes with units: "Revenue ($M)", "Count (thousands)"
- Bar charts: Y-axis starts at zero (truncated axes exaggerate)
- Line charts may start non-zero when range matters more
- Use abbreviations: $1.2M, 45K, 3.5B
- Dates: abbreviated month (Jan, Feb), never numbers
Legends & Annotations
- Direct labeling > legend — label series on the chart when possible
- Annotate key events on time series (launches, incidents)
- Call out key values — data labels on important points, not all
Sizing & Density
- Aspect ratio: wider than tall (16:9 or 4:3)
- One chart = one message — if it says two things, split it
- Max 5-7 series per chart
- Highlight the insight — gray context, color the story
Typography in Charts
Title: 14-16pt bold, left-aligned above chart Subtitle: 10-12pt regular, gray Axis labels: 8-9pt gray Data labels: 7-8pt near data points Legend: 8-9pt, bottom or right Source: 7pt gray, bottom-left
🛠️ Implementation: Matplotlib (for python-pptx)
import matplotlib.pyplot as plt import matplotlib.ticker as mticker import io WMT_COLORS = { 'blue': '#0053E2', 'spark': '#FFC220', 'green': '#2A8703', 'red': '#EA1100', 'cyan': '#0096D6', 'gray_160': '#2E2F32', 'gray_100': '#74767B', 'gray_50': '#D9D9D9', 'gray_10': '#F5F5F5', } def set_walmart_style(): plt.rcParams.update({ 'font.family': 'sans-serif', 'font.sans-serif': ['Segoe UI', 'Helvetica', 'Arial'], 'font.size': 10, 'axes.titlesize': 14, 'axes.titleweight': 'bold', 'axes.labelsize': 9, 'axes.labelcolor': '#74767B', 'axes.edgecolor': '#D9D9D9', 'axes.facecolor': 'white', 'axes.grid': True, 'axes.grid.axis': 'y', 'grid.color': '#D9D9D9', 'grid.linewidth': 0.5, 'grid.alpha': 0.7, 'xtick.labelsize': 8, 'ytick.labelsize': 8, 'xtick.color': '#74767B', 'ytick.color': '#74767B', 'legend.fontsize': 8, 'legend.frameon': False, 'figure.facecolor': 'white', 'figure.dpi': 150, 'savefig.bbox': 'tight', 'savefig.pad_inches': 0.1, }) def fig_to_bytes(fig, dpi=150) -> io.BytesIO: buf = io.BytesIO() fig.savefig(buf, format='png', dpi=dpi, bbox_inches='tight', facecolor='white', edgecolor='none') buf.seek(0); plt.close(fig); return buf
Bar chart:
exec_bar_chart(categories, values, title, ylabel, color, target, pct, figsize)
Line chart: exec_line_chart(dates, series_dict, title, ylabel, pct, figsize)
KPI card: exec_kpi_card(metric_name, value, target, unit, figsize)
(Full implementations in
src/pptx_analyzer.py → create_chart_image())
MBR-Specific Charts (in src/pptx_analyzer.py
)
src/pptx_analyzer.py
— segments × dimensions with RAG colormaporg_health_heatmap()
— voluntary + involuntary stacked decompositionturnover_waterfall()
— histogram with avg line + optimal range shadingspan_distribution()
🛠️ Implementation: Chart.js (for HTML reports)
// CRITICAL: Canvas ignores height when responsive: true // ALWAYS wrap in fixed-height container // ✅ <div style="height: 400px; position: relative;"><canvas id="c"></canvas></div> // ❌ <canvas id="c" height="400"></canvas> const walmartTheme = { colors: ['#0053E2', '#FFC220', '#2A8703', '#0096D6', '#76238C', '#EA1100'], fontFamily: "'Segoe UI', 'Helvetica', sans-serif", gridColor: '#D9D9D9', textColor: '#74767B', }; // Standard config pattern options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { usePointStyle: true, padding: 20, font: { size: 11 } } }, title: { display: true, text: 'Title', font: { size: 16, weight: 'bold' }, color: '#2E2F32', align: 'start' }, tooltip: { backgroundColor: '#2E2F32', padding: 12, cornerRadius: 8, callbacks: { label: ctx => `${ctx.dataset.label}: ${ctx.parsed.y.toLocaleString()}` } }, }, scales: { y: { beginAtZero: true, grid: { color: '#D9D9D9' }, ticks: { callback: v => v >= 1000 ? `${(v/1000).toFixed(0)}K` : v } }, x: { grid: { display: false } }, }, }
Combo chart: Use
type: 'bar' with a type: 'line' dataset + borderDash: [5,5] for targets + order to layer.
KPI Card HTML:
<div class="bg-white rounded-xl shadow-sm border p-6"> <div class="text-sm text-gray-500 uppercase tracking-wide">Total Revenue</div> <div class="text-3xl font-bold text-gray-900 mt-1">$14.2M</div> <div class="flex items-center mt-2"> <span class="text-green-600 font-medium">▲ 12.3%</span> <span class="text-gray-400 text-sm ml-2">vs prior period</span> </div> </div>
♿ Accessibility (WCAG 2.2 AA)
- Never color alone — add labels, patterns, icons
- 4.5:1 contrast for text, 3:1 for chart elements
- Color-blind safe — test with deuteranopia simulator
- Alt text on all embedded images
- Descriptive titles — state the insight, not the topic
- Data tables in appendix for complex charts
- Pattern fills as secondary encoding (stripes, dots)
# Set alt text on PPTX image shapes: def set_alt_text(shape, title, description): from pptx.oxml.ns import qn sp = shape._element nvPr = sp.find(qn('p:nvSpPr')) or sp.find(qn('p:nvPicPr')) if nvPr: cNvPr = nvPr.find(qn('p:cNvPr')) if cNvPr: cNvPr.set('title', title) cNvPr.set('descr', description)
🚫 Anti-Patterns (Never Do These)
- ❌ Pie chart >5 slices → use horizontal bar
- ❌ 3D charts → always distort perception
- ❌ Dual Y-axes different scales → misleading; use two charts
- ❌ Rainbow palettes → no semantic meaning
- ❌ Truncated bar chart Y-axis → exaggerates differences
- ❌ Unlabeled axes → reader can't interpret
- ❌ Too many gridlines → use subtle/few
- ❌ Legend far from data → increases cognitive load
- ❌ Default Chart.js styling → always customize
- ❌ Vertical labels → rotate chart or use horizontal bars