Awesome-Agent-Skills-for-Empirical-Research color-accessibility-guide
Colorblind-friendly palettes and accessible visualization design
git clone https://github.com/brycewang-stanford/Awesome-Agent-Skills-for-Empirical-Research
T=$(mktemp -d) && git clone --depth=1 https://github.com/brycewang-stanford/Awesome-Agent-Skills-for-Empirical-Research "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/43-wentorai-research-plugins/skills/analysis/dataviz/color-accessibility-guide" ~/.claude/skills/brycewang-stanford-awesome-agent-skills-for-empirical-research-color-accessibili && rm -rf "$T"
skills/43-wentorai-research-plugins/skills/analysis/dataviz/color-accessibility-guide/SKILL.mdColor Accessibility Guide
Design data visualizations that are accessible to colorblind readers and follow best practices for clarity, using tested palettes and encoding principles.
Color Vision Deficiency Overview
Approximately 8% of males and 0.5% of females have some form of color vision deficiency (CVD). The most common types:
| Type | Prevalence (Male) | Affected Colors | Commonly Confused |
|---|---|---|---|
| Deuteranomaly (green-weak) | 5% | Green | Red and green |
| Protanomaly (red-weak) | 1% | Red | Red and green |
| Deuteranopia (no green) | 1% | Green | Red and green |
| Protanopia (no red) | 1% | Red | Red and green |
| Tritanopia (no blue) | 0.003% | Blue | Blue and yellow |
| Monochromacy | Very rare | All | All colors |
Key takeaway: Never rely solely on a red-green distinction to convey information. About 1 in 12 male readers cannot distinguish them.
Recommended Colorblind-Safe Palettes
Qualitative Palettes (Categorical Data)
Wong (2011) Nature Palette (8 colors)
Widely recommended for scientific publications:
# Wong's colorblind-friendly palette wong_palette = { "black": "#000000", "orange": "#E69F00", "sky_blue": "#56B4E9", "bluish_green":"#009E73", "yellow": "#F0E442", "blue": "#0072B2", "vermillion": "#D55E00", "reddish_purple":"#CC79A7" }
Okabe-Ito Palette
okabe_ito = ["#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7", "#000000"]
Tol's Qualitative Palette
# Paul Tol's qualitative palette (up to 12 distinct colors) tol_qualitative = ["#332288", "#88CCEE", "#44AA99", "#117733", "#999933", "#DDCC77", "#CC6677", "#882255", "#AA4499", "#661100", "#6699CC", "#888888"]
Sequential Palettes (Ordered Data)
For continuous data, use perceptually uniform colormaps:
import matplotlib.pyplot as plt # Recommended sequential colormaps # These are perceptually uniform and colorblind-safe: good_cmaps = ["viridis", "plasma", "inferno", "magma", "cividis"] # Avoid these (not perceptually uniform, not colorblind-safe): bad_cmaps = ["jet", "rainbow", "hsv"] # NEVER use these # Example usage import numpy as np data = np.random.randn(10, 10) fig, ax = plt.subplots(figsize=(8, 6)) im = ax.imshow(data, cmap="viridis") plt.colorbar(im) plt.title("Use viridis, not jet") plt.savefig("heatmap.pdf", dpi=300, bbox_inches="tight")
Diverging Palettes (Data with Meaningful Center)
# Colorblind-safe diverging palettes # Blue-to-Red via white (good for temperature, correlation) import matplotlib.colors as mcolors # Built-in matplotlib options: diverging_safe = ["RdBu_r", "PuOr_r", "BrBG"] # Custom two-color diverging (Tol): tol_diverging = ["#364B9A", "#4A7BB7", "#6EA6CD", "#98CAE1", "#C2E4EF", "#EAECCC", "#FEDA8B", "#FDB366", "#F67E4B", "#DD3D2D", "#A50026"]
Design Principles for Accessible Visualization
1. Data-Ink Ratio
Edward Tufte's principle: maximize the proportion of ink used to display actual data.
import matplotlib.pyplot as plt fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) # BAD: Low data-ink ratio (chartjunk) ax1.bar(range(5), [3, 7, 2, 5, 8], color="blue", edgecolor="black", linewidth=2) ax1.set_facecolor("#EEEEEE") ax1.grid(True, color="white", linewidth=2) ax1.set_title("Before: Low Data-Ink Ratio") # GOOD: High data-ink ratio ax2.bar(range(5), [3, 7, 2, 5, 8], color="#0072B2", edgecolor="none") ax2.spines["top"].set_visible(False) ax2.spines["right"].set_visible(False) ax2.set_title("After: High Data-Ink Ratio") plt.tight_layout() plt.savefig("data_ink_ratio.pdf", dpi=300)
2. Redundant Encoding
Never use color as the sole channel for conveying information. Combine color with at least one other visual channel:
| Channel | Examples |
|---|---|
| Shape | Circles, squares, triangles for different groups |
| Pattern | Solid, dashed, dotted lines |
| Fill pattern | Hatching, cross-hatching for bar charts |
| Label | Direct text labels on or near data points |
| Position | Separate panels (facets) for each group |
| Size | Varying point sizes |
import matplotlib.pyplot as plt markers = ['o', 's', '^', 'D'] # Different shapes colors = ['#0072B2', '#D55E00', '#009E73', '#CC79A7'] labels = ['Group A', 'Group B', 'Group C', 'Group D'] fig, ax = plt.subplots(figsize=(8, 6)) for i in range(4): ax.scatter(x[i], y[i], c=colors[i], marker=markers[i], s=80, label=labels[i], edgecolors='black', linewidth=0.5) ax.legend() ax.set_xlabel("X Variable") ax.set_ylabel("Y Variable") plt.savefig("redundant_encoding.pdf", dpi=300)
3. Line Style Differentiation
line_styles = ['-', '--', '-.', ':', (0, (3, 1, 1, 1))] colors = ['#0072B2', '#D55E00', '#009E73', '#CC79A7', '#E69F00'] fig, ax = plt.subplots(figsize=(8, 5)) for i in range(5): ax.plot(x, data[i], color=colors[i], linestyle=line_styles[i], linewidth=2, label=f"Method {i+1}") ax.legend()
Checking Your Visualizations
Simulation Tools
| Tool | Platform | URL |
|---|---|---|
| Coblis | Web | color-blindness.com/coblis |
| Color Oracle | Desktop (Win/Mac/Linux) | colororacle.org |
| Sim Daltonism | macOS | michelf.ca/projects/sim-daltonism |
| Colorblindly | Chrome extension | Chrome Web Store |
| Matplotlib CVD simulation | Python | See code below |
Programmatic CVD Simulation
from colorspacious import cspace_convert import numpy as np def simulate_cvd(rgb_hex, deficiency="deuteranomaly", severity=100): """Simulate how a color appears to someone with CVD.""" # Convert hex to RGB [0,1] rgb = np.array([int(rgb_hex[i:i+2], 16)/255 for i in (1, 3, 5)]) # Convert using colorspacious cvd_space = {"name": "sRGB1+CVD", "cvd_type": deficiency, "severity": severity} rgb_cvd = cspace_convert(rgb, cvd_space, "sRGB1") rgb_cvd = np.clip(rgb_cvd, 0, 1) return "#{:02x}{:02x}{:02x}".format(*[int(c*255) for c in rgb_cvd]) # Test your palette for color in ["#FF0000", "#00FF00", "#0072B2", "#D55E00"]: sim = simulate_cvd(color) print(f"{color} -> {sim} (deuteranomaly)")
Quick Reference: Do's and Don'ts
| Do | Don't |
|---|---|
| Use Wong or Okabe-Ito palettes | Use red vs. green to distinguish categories |
| Use viridis/cividis colormaps | Use jet/rainbow colormaps |
| Add shape/pattern as redundant encoding | Rely on color alone |
| Use direct labels when possible | Force readers to match colors to legend repeatedly |
| Test with CVD simulators | Assume your color choices work for everyone |
| Use high contrast (WCAG AA: 4.5:1 ratio) | Use light colors on white backgrounds |
| Keep maximum 7-8 colors in categorical charts | Use 15+ colors that are impossible to distinguish |