AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Visualization Patterns

skill-ai-analyst-lab-ai-analyst-plugin-visualization-patterns · by ai-analyst-lab

>-

— No reviews yet
0 installs
15 views
0.0% view→install

Install

$ agentstack add skill-ai-analyst-lab-ai-analyst-plugin-visualization-patterns

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • ✓ Prompt-injection patterns
  • ✓ Secret / credential exfiltration
  • ✓ Dangerous shell & filesystem operations
  • ✓ Untrusted network calls
  • ✓ Known-malicious package signatures

What it can access

  • ✓ Network access No
  • ✓ Filesystem access No
  • ✓ Shell / process execution No
  • ✓ Environment & secrets No
  • ✓ Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-ai-analyst-lab-ai-analyst-plugin-visualization-patterns)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● 1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Visualization Patterns? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Skill: Visualization Patterns

Purpose

Ensure every chart this analyst produces follows high-quality design standards with named themes, consistent styling, and clear data communication.

When to Use

Apply this skill whenever generating a chart, graph, or data visualization.

Default Theme: Minimal

Unless the user specifies otherwise, ALWAYS use the minimal theme. It's clean, professional, and suitable for most business contexts:

  • Warm off-white background (#F7F6F2) for reduced eye strain
  • Action Amber accent (#D97706) for focus
  • Helvetica font family
  • Left-aligned titles, minimal gridlines

The SWD base style (Step 1) is the minimal theme. Other available themes: nyt, economist, corporate (see Theme Definitions section).

When no theme or palette is set, use the default styling without asking; offer palette options only when the user asks about themes or colors.

Instructions

STEP 1: Apply the SWD Base Style (DO THIS FIRST)

Before writing ANY charting code, set the SWD base style yourself. Apply these rcParams at the top of every chart script:

import matplotlib.pyplot as plt

plt.rcParams.update({
    "figure.figsize": (8, 5),
    "figure.dpi": 150,
    "figure.facecolor": "#F7F6F2",
    "axes.facecolor": "#F7F6F2",
    "axes.spines.top": False,
    "axes.spines.right": False,
    "axes.grid": False,
    "font.family": "sans-serif",
    "font.size": 10,
    "axes.titlesize": 14,
    "axes.titleweight": "bold",
})

COLORS = {
    "action": "#D97706",   # Action Amber, the focus color
    "accent": "#DC2626",   # Accent Red, secondary callout
    "success": "#059669",
    "gray900": "#1F2937",  # labels and strong text
    "gray600": "#6B7280",  # subtitles
    "gray400": "#9CA3AF",  # muted labels
    "gray200": "#E5E7EB",  # non-highlighted data, light gridlines
    "bg": "#F7F6F2",
}

STEP 2: Build the Chart With the SWD Patterns

Implement these patterns directly (they take a matplotlib ax plus arrays of data). Always create the figure first: fig, ax = plt.subplots(figsize=(10, 6)).

Highlight-bar pattern (bar chart, one bar highlighted, rest gray):

  • Horizontal bars by default (ax.barh), sorted by value
  • Every bar is gray200 except the highlighted category, which gets action amber
  • Hide the value axis entirely (ax.xaxis.set_visible(False) and hide that spine); extend the limit to 1.15x the max
  • Put direct value labels at the end of each bar (fontsize 9, gray900), offset by ~2% of the max value

Highlight-line pattern (line chart, one series colored, rest gray):

  • Draw non-highlighted series first in gray200, linewidth 1.2, with a small end-of-line label in gray400 (fontsize 8)
  • Draw the highlighted series on top in action amber, linewidth 2.5, with a bold end-of-line label in the highlight color (fontsize 9)
  • Light horizontal y-gridlines only (gray200, linewidth 0.5, ax.set_axisbelow(True))

Action title pattern:

  • Title is the takeaway statement, bold, left-aligned, fontsize ~17, gray900: use ax.set_title(title, loc="left", y=1.12). Position with y, not pad (pad collides with subtitles).
  • Optional subtitle in gray600, fontsize 12, just below the title (e.g. ax.text(0, 1.06, subtitle, transform=ax.transAxes, ...) with the title at y=1.12)

For funnel charts, heatmaps, or custom visualizations: use manual matplotlib, but apply the Step 1 base style first and follow the SWD principles below.

STEP 3: Save Chart to Correct Location

Save with tight layout and correct DPI, then close the figure:

# Final deliverable charts sit next to the brief in the working folder
fig.savefig("conversion_by_device.png", dpi=150, bbox_inches="tight")
plt.close(fig)

# Exploratory/intermediate charts go to working/ instead:
# fig.savefig("working/conversion_by_device_draft.png", dpi=150, bbox_inches="tight")

Naming convention: {metric}_{dimension}_{chart_type}.png (e.g., revenue_trends_line.png)

Pre-flight: Load Learnings (Optional)

Check .knowledge/learnings/index.md for relevant entries:

  • Read the file. If it doesn't exist or is empty, skip silently.
  • Scan for entries under "Chart Style" and "General" headings.
  • If entries exist, incorporate them as constraints (e.g., preferred chart types, color overrides).
  • Never block execution if learnings are unavailable.

Core Principle: Storytelling with Data (SWD)

Every chart follows the SWD methodology by Cole Nussbaumer Knaflic:

> Gray everything first. Color is reserved for the one data point that tells the story.

  • Maximum 2 colors + gray per chart. Action Amber (#D97706) for the primary focus, Accent Red (#DC2626) for a secondary callout. Everything else is gray.
  • Titles state the takeaway, not a description. "iOS drove the June ticket spike" not "Tickets by Platform."
  • Every visual element must earn its place — if it doesn't help the reader understand the story, remove it.
  • Prefer text over charts for single numbers. Prefer horizontal bars over pie charts. Prefer direct labels over legends.

Why use the patterns above: They enforce these principles mechanically. Ad-hoc matplotlib code often forgets to remove borders, uses rainbow colors, or includes legends. Following the Step 1-2 patterns prevents these mistakes.

Declutter Checklist

Before finalizing any chart, verify each item:

  • [ ] Chart border / box — removed entirely
  • [ ] Top and right spines — removed (keep only bottom and left)
  • [ ] Heavy gridlines — removed or very light gray (#E5E7EB), y-axis only
  • [ ] Data markers — removed from line charts (the line is the data)
  • [ ] Legend — replaced with direct labels on the data
  • [ ] Rotated axis text — if labels need rotation, switch to horizontal bars
  • [ ] Trailing zeros — use $45 not $45.00; use 12% not 12.0%
  • [ ] 3D effects — never
  • [ ] Background color — always warm off-white (#F7F6F2)
  • [ ] Redundant axis labels — if the title says "Revenue ($M)", the y-axis doesn't need "Revenue in Millions of Dollars"
  • [ ] Excessive tick marks — reduce to 4-6 ticks maximum
  • [ ] Decimal precision — match the precision to the decision (12% not 12.347%)

Chart Sequencing (Multi-Chart Analyses)

When producing multiple charts for a deep dive or root cause investigation, follow Context → Tension → Resolution:

| Phase | Charts | Purpose | Example | |-------|--------|---------|---------| | Context | 1-2 | Set the baseline. What does normal look like? | "[Dataset] processes ~4,000 support tickets per month" | | Tension | 2-3 | Reveal the problem. Progressively zoom in. | "June spiked to 6,200" → "The spike was iOS payment issues" | | Resolution | 1-2 | Explain why and recommend action. | "iOS v2.3 introduced a bug → fix eliminates ~2,200 tickets/mo" |

  • Each chart builds on the previous one
  • Never show a chart that makes the audience ask "so what?"
  • The number of charts is determined by the storyboard. Each narrative beat that requires a visualization becomes a chart.
  • The final chart should make the recommended action obvious

SWD Building Blocks Reference

These are the building blocks to implement in your chart code (Steps 1-3 above spell out the mechanics):

| Building block | Purpose | Key mechanics | |----------------|---------|---------------| | SWD base style | Base matplotlib rcParams + palette | Step 1 rcParams block and COLORS dict | | Highlight-bar pattern | Bar chart with one bar highlighted, rest gray | horizontal, sorted, hidden value axis, direct labels | | Highlight-line pattern | Line chart with one line colored, rest gray | gray series behind, amber on top, end-of-line labels | | Action title pattern | Bold takeaway title + optional subtitle | left-aligned, title states the finding | | Point annotation | Clean annotation with arrow | ax.annotate with a thin gray arrow, minimal text | | Save convention | Tight layout + correct DPI | fig.savefig(path, dpi=150, bbox_inches="tight") then close |

Theme Definitions

Theme: nyt (New York Times)
NYT_THEME = {
    "colors": {
        "primary": "#000000",
        "secondary": "#666666",
        "accent": "#D03A2B",
        "palette": ["#D03A2B", "#1A6B54", "#3D6CA3", "#E8912D", "#8B5E3C", "#6B4C9A"],
        "background": "#FFFFFF",
        "grid": "#E5E5E5",
    },
    "fonts": {
        "title": {"family": "Georgia", "size": 18, "weight": "bold"},
        "subtitle": {"family": "Arial", "size": 12, "weight": "normal", "color": "#666666"},
        "axis_label": {"family": "Arial", "size": 10},
        "annotation": {"family": "Arial", "size": 9, "style": "italic"},
    },
    "grid": {"show": True, "axis": "y", "style": "--", "alpha": 0.3},
    "annotations": {"style": "minimal", "callout_arrows": True},
    "title": {"position": "left-aligned", "include_subtitle": True},
}
Theme: economist (The Economist)
ECONOMIST_THEME = {
    "colors": {
        "primary": "#1F2E3C",
        "secondary": "#7C8A96",
        "accent": "#E3120B",
        "palette": ["#E3120B", "#1F6ED4", "#36B37E", "#F5A623", "#6554C0", "#00B8D9"],
        "background": "#D7E4E8",
        "grid": "#FFFFFF",
    },
    "fonts": {
        "title": {"family": "Helvetica", "size": 16, "weight": "bold"},
        "subtitle": {"family": "Helvetica", "size": 11, "weight": "normal"},
        "axis_label": {"family": "Helvetica", "size": 9},
        "annotation": {"family": "Helvetica", "size": 8},
    },
    "grid": {"show": True, "axis": "y", "style": "-", "alpha": 0.5, "color": "#FFFFFF"},
    "annotations": {"style": "inline", "red_highlight": True},
    "title": {"position": "left-aligned", "red_bar_top": True},
}
Theme: minimal
MINIMAL_THEME = {
    "colors": {
        "primary": "#1F2937",
        "secondary": "#6B7280",
        "accent": "#D97706",   # Action Amber — same focus color as the Step 1 base style
        "palette": ["#D97706", "#DC2626", "#059669", "#2563EB", "#7C3AED", "#DB2777"],
        "background": "#F7F6F2",
        "grid": "#E5E7EB",
    },
    "fonts": {
        "title": {"family": "Helvetica", "size": 14, "weight": "bold"},
        "subtitle": {"family": "Helvetica", "size": 10, "weight": "normal", "color": "#666666"},
        "axis_label": {"family": "Helvetica", "size": 9},
        "annotation": {"family": "Helvetica", "size": 8},
    },
    "grid": {"show": True, "axis": "y", "style": "-", "alpha": 0.15},
    "annotations": {"style": "minimal", "direct_labels": True},
    "title": {"position": "left-aligned", "include_subtitle": True},
}
Theme: corporate
CORPORATE_THEME = {
    "colors": {
        "primary": "#1B2A4A",
        "secondary": "#5A6B7F",
        "accent": "#0066CC",
        "palette": ["#0066CC", "#00A651", "#FF6600", "#CC0000", "#9933CC", "#00CCCC"],
        "background": "#FFFFFF",
        "grid": "#E8E8E8",
    },
    "fonts": {
        "title": {"family": "Arial", "size": 16, "weight": "bold"},
        "subtitle": {"family": "Arial", "size": 11, "weight": "normal"},
        "axis_label": {"family": "Arial", "size": 10},
        "annotation": {"family": "Arial", "size": 9},
    },
    "grid": {"show": True, "axis": "both", "style": "-", "alpha": 0.2},
    "annotations": {"style": "callout", "box_highlight": True},
    "title": {"position": "center", "include_subtitle": True},
}

Applying a Theme (matplotlib)

import matplotlib.pyplot as plt
import matplotlib.ticker as mticker

def apply_theme(fig, ax, theme):
    """Apply a named theme to a matplotlib figure."""
    fig.patch.set_facecolor(theme["colors"]["background"])
    ax.set_facecolor(theme["colors"]["background"])

    # Title styling
    ax.set_title(
        ax.get_title(),
        fontfamily=theme["fonts"]["title"]["family"],
        fontsize=theme["fonts"]["title"]["size"],
        fontweight=theme["fonts"]["title"]["weight"],
        loc="left" if theme["title"]["position"] == "left-aligned" else "center",
        pad=15,
    )

    # Grid
    if theme["grid"]["show"]:
        ax.grid(
            axis=theme["grid"]["axis"],
            linestyle=theme["grid"]["style"],
            alpha=theme["grid"]["alpha"],
            color=theme["colors"].get("grid", "#E0E0E0"),
        )
        ax.set_axisbelow(True)

    # Clean spines
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.spines["left"].set_alpha(0.3)
    ax.spines["bottom"].set_alpha(0.3)

    # Axis labels
    ax.xaxis.label.set_fontfamily(theme["fonts"]["axis_label"]["family"])
    ax.xaxis.label.set_fontsize(theme["fonts"]["axis_label"]["size"])
    ax.yaxis.label.set_fontfamily(theme["fonts"]["axis_label"]["family"])
    ax.yaxis.label.set_fontsize(theme["fonts"]["axis_label"]["size"])

    plt.tight_layout()

Chart Type Selection

| Data Relationship | Chart Type | When to Use | |---|---|---| | Comparison (categories) | Bar chart (vertical) | Comparing ≤12 categories | | Comparison (many categories) | Bar chart (horizontal) | Comparing >7 categories or long labels | | Comparison (parts of whole) | Stacked bar | Showing composition across categories | | Change over time | Line chart | Continuous time series, trends | | Change over time (few periods) | Bar chart | Discrete periods (quarters, years) | | Correlation | Scatter plot | Relationship between two continuous variables | | Distribution | Histogram | Single variable distribution | | Distribution (compare groups) | Box plot or violin | Distribution comparison across groups | | Proportion | Donut chart | ≤5 segments, one variable | | Flow/Process | Funnel chart | Conversion or drop-off rates | | Intensity | Heatmap | Two categorical dimensions + one value | | Cumulative | Area chart | Running totals over time | | Ranking changes | Bump chart | Rank position changes over time | | Waterfall | Waterfall chart | Additive/subtractive contributions |

Annotation Standards

  1. Always label key data points directly — do not rely on legends for primary story elements
  2. Use direct labels on bars and line endpoints instead of requiring axis reading
  3. Annotate inflection points — mark where trends change with a brief note
  4. Titles are takeaways, not descriptions — "Revenue grew 23% after launch" not "Revenue by Month"
  5. Subtitles provide context — "Monthly revenue, Jan–Dec 2025, in $M"
  6. Source line at bottom-left in small gray text
  7. Format numbers for readability — "$1.2M" not "$1,234,567"; "23%" not "0.2345"
  8. Max 6 colors in any single chart — use gray for "other" or "rest"
  9. Highlight the story — use accent color for the key data point, gray for context

Standard Chart Setup

def create_chart(data, chart_type, theme_name="minimal", title="", subtitle=""):
    """Standard chart creation pattern."""
    theme = {"nyt": NYT_THEME, "economist": ECONOMIST_THEME,
             "minimal": MINIMAL_THEME, "corporate": CORPORATE_THEME}[theme_name]

    fig, ax = plt.subplots(figsize=(10, 6))
    fig.patch.set_facecolor(theme["colors"]["background"])
    ax.set_facecolor(theme["colors"]["background"])

    # Plot data using theme colors
    colors = theme["colors"]["palette"]

    # Set title as takeaway
    ax.set_title(title, fontfamily=theme["fonts"]["title"]["family"],
                 fontsize=theme["fonts"]["title"]["size"],
                 fontweight=theme["fonts"]["title"]["weight"],
                 loc="left", pad=20)
    # Subtitle
    if subtitle:
        ax.text(0, 1.02, subtitle, transform=ax.transAxes,
                fontfamily=theme["fonts"]["subtitle"]["family"],
                fontsize=theme["fonts"]["subtitle"]["size"],
                color=theme["fonts"]["subtitle"].get("color", "#666666"))

    apply_theme(fig, ax, theme)
    return fig, ax

Examples

Example 1: Bar chart with NYT theme


…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [ai-analyst-lab](https://github.com/ai-analyst-lab)
- **Source:** [ai-analyst-lab/ai-analyst-plugin](https://github.com/ai-analyst-lab/ai-analyst-plugin)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.