Professional Plotly visualization best practices for Field Agents including chart specifications, color palettes, formatting standards, and JSON structure requirements for executive-ready data visualizations
56
64%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./field-agent-skills/visualization/SKILL.mdThis skill provides comprehensive guidelines for creating professional, executive-ready visualizations for Field Agents using Plotly. Follow these standards to ensure clean, readable, and impactful data visualizations.
Use this skill when you need to:
Always use this Treasure Data color palette for consistency:
TD_COLORS = [
'#44BAB8', # Teal (Primary)
'#8FD6D4', # Light Teal
'#DAF1F1', # Pale Teal
'#2E41A6', # Navy Blue
'#828DCA', # Purple
'#D5D9ED', # Light Purple
'#8CC97E', # Green
'#BADFB2', # Light Green
'#E8F4E5', # Pale Green
'#EEB53A', # Accent Yellow
'#F5D389', # Light Yellow
'#5FCFD8', # Cyan
'#A05EB0', # Magenta
'#C69ED0' # Light Magenta
]Usage:
#44BAB8 (Teal) as primary color for single-series charts#2E41A6 (Navy) for text and titlesFor charts requiring RGB/RGBA format (e.g., transparency effects):
def hex_to_rgb(hex_color):
"""Convert hex color to RGB tuple
Args:
hex_color (str): Hex color code (e.g., '#44BAB8' or '44BAB8')
Returns:
tuple: RGB values as (R, G, B) where each value is 0-255
Example:
>>> hex_to_rgb('#44BAB8')
(68, 186, 184)
"""
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
def hex_to_rgba(hex_color, alpha=1.0):
"""Convert hex color to RGBA string for Plotly
Args:
hex_color (str): Hex color code (e.g., '#44BAB8')
alpha (float): Opacity value 0.0-1.0
Returns:
str: RGBA color string (e.g., 'rgba(68, 186, 184, 0.5)')
Example:
>>> hex_to_rgba('#44BAB8', 0.5)
'rgba(68, 186, 184, 0.5)'
"""
r, g, b = hex_to_rgb(hex_color)
return f'rgba({r}, {g}, {b}, {alpha})'
# Usage examples:
td_primary_rgb = hex_to_rgb('#44BAB8') # (68, 186, 184)
td_primary_transparent = hex_to_rgba('#44BAB8', 0.3) # 'rgba(68, 186, 184, 0.3)'Always use proper JSON objects with native arrays and objects:
{
"data": [
{
"type": "bar",
"x": ["A", "B", "C"],
"y": [10, 20, 30],
"marker": {"color": "#44BAB8"},
"text": ["10", "20", "30"],
"textposition": "outside",
"textfont": {"size": 11, "color": "black"}
}
],
"layout": {
"title": {
"text": "Chart Title",
"x": 0.5,
"font": {"size": 18, "color": "#2E41A6"}
},
"height": 500,
"showlegend": true,
"margin": {"t": 120, "b": 80, "l": 80, "r": 80},
"font": {"family": "Arial", "size": 12},
"plot_bgcolor": "white",
"paper_bgcolor": "white"
}
}{
"data": "[{\"type\": \"bar\", \"x\": [\"A\", \"B\"], \"y\": [10, 20]}]"
}Critical: Data must be JSON objects and arrays, NOT stringified JSON.
{
"data": [{
"type": "bar",
"x": ["Category A", "Category B", "Category C"],
"y": [45, 30, 25],
"name": "Series Name",
"marker": {"color": "#44BAB8"},
"text": [45, 30, 25], // ⚠️ CRITICAL: Must include for numbers on bars
"textposition": "outside", // ⚠️ CRITICAL: Shows numbers above bars
"textfont": {"size": 11, "color": "black"}
}],
"layout": {
"title": {"text": "Bar Chart Title", "x": 0.5, "font": {"size": 18}},
"height": 500,
"showlegend": true, // ⚠️ CRITICAL: Must be true for multi-series
"legend": {
"orientation": "h",
"yanchor": "bottom",
"y": 1.05, // ⚠️ CRITICAL: Must be above 1.0 to be visible
"xanchor": "center",
"x": 0.5
},
"margin": {"t": 120, "b": 80, "l": 80, "r": 80}, // ⚠️ CRITICAL: Extra top margin for legend
"xaxis": {"title": {"text": "Categories", "font": {"size": 14}}},
"yaxis": {"title": {"text": "Values", "font": {"size": 14}}},
"font": {"family": "Arial", "size": 12},
"plot_bgcolor": "white",
"paper_bgcolor": "white"
}
}{
"data": [{
"type": "heatmap",
"x": ["Col A", "Col B", "Col C"],
"y": ["Row 1", "Row 2", "Row 3"],
"z": [[23.4, 45.6, 12.3], [34.5, 56.7, 23.1], [45.2, 67.8, 34.5]],
"text": [[23.4, 45.6, 12.3], [34.5, 56.7, 23.1], [45.2, 67.8, 34.5]], // ⚠️ CRITICAL: Numbers to display
"texttemplate": "%{text:.1f}", // ⚠️ CRITICAL: Format to 1 decimal
"textfont": {"size": 12, "color": "black"}, // ⚠️ CRITICAL: Visible text
"showscale": true, // ⚠️ CRITICAL: Show color scale
"colorscale": [
[0, "#DAF1F1"],
[0.5, "#8FD6D4"],
[1, "#44BAB8"]
],
"colorbar": {
"title": {"text": "Value", "font": {"size": 12}},
"titleside": "right"
},
"hovertemplate": "<b>%{y}</b> - <b>%{x}</b><br>Value: %{z:.1f}<extra></extra>"
}],
"layout": {
"title": {"text": "Heatmap Title", "x": 0.5, "font": {"size": 18}},
"height": 500,
"xaxis": {"title": {"text": "X Axis", "font": {"size": 14}}},
"yaxis": {"title": {"text": "Y Axis", "font": {"size": 14}}},
"margin": {"t": 80, "b": 80, "l": 80, "r": 100},
"font": {"family": "Arial", "size": 12},
"plot_bgcolor": "white",
"paper_bgcolor": "white"
}
}Before generating any chart, verify:
"text", "textposition": "outside", "textfont""showlegend": true, legend "y": 1.05 or higher"t": 120 minimum for horizontal legends"text", "texttemplate": "%{text:.1f}", "showscale": true"textfont": {"color": "black"} for contrastPie charts ALWAYS require visible legends.
{
"data": [{
"type": "pie",
"values": [45, 30, 25],
"labels": ["Channel A", "Channel B", "Channel C"],
"marker": {
"colors": ["#44BAB8", "#8FD6D4", "#DAF1F1"]
},
"textinfo": "label+percent",
"textposition": "auto",
"textfont": {"size": 14, "color": "black"},
"hovertemplate": "<b>%{label}</b><br>Value: %{value}<br>Percentage: %{percent}<extra></extra>"
}],
"layout": {
"title": {
"text": "Attribution Distribution",
"x": 0.5,
"font": {"size": 18, "color": "#2E41A6"}
},
"height": 500,
"showlegend": true,
"legend": {
"orientation": "v",
"yanchor": "middle",
"y": 0.5,
"xanchor": "left",
"x": 1.02,
"font": {"size": 12}
},
"margin": {"t": 80, "b": 80, "l": 80, "r": 150},
"font": {"family": "Arial", "size": 12},
"paper_bgcolor": "white"
}
}Key Points:
x: 1.02)r: 150) for legend spaceMulti-series bar charts require horizontal legends positioned above the chart.
{
"data": [
{
"type": "bar",
"x": ["Channel A", "Channel B", "Channel C"],
"y": [45, 30, 25],
"name": "Metric 1",
"marker": {"color": "#44BAB8"},
"text": [45, 30, 25],
"textposition": "outside",
"textfont": {"size": 11, "color": "black"}
},
{
"type": "bar",
"x": ["Channel A", "Channel B", "Channel C"],
"y": [35, 40, 30],
"name": "Metric 2",
"marker": {"color": "#8FD6D4"},
"text": [35, 40, 30],
"textposition": "outside",
"textfont": {"size": 11, "color": "black"}
}
],
"layout": {
"title": {
"text": "Channel Comparison",
"x": 0.5,
"font": {"size": 18, "color": "#2E41A6"}
},
"height": 500,
"barmode": "group",
"showlegend": true,
"legend": {
"orientation": "h",
"yanchor": "bottom",
"y": 1.05,
"xanchor": "center",
"x": 0.5,
"font": {"size": 12}
},
"xaxis": {
"title": {"text": "Channels", "font": {"size": 14, "color": "#2E41A6"}}
},
"yaxis": {
"title": {"text": "Performance %", "font": {"size": 14, "color": "#2E41A6"}}
},
"margin": {"t": 120, "b": 80, "l": 80, "r": 80},
"font": {"family": "Arial", "size": 12},
"plot_bgcolor": "white",
"paper_bgcolor": "white"
}
}Key Points:
barmode to "group" for side-by-side or "stack" for stackedy: 1.05)t: 120){
"data": [{
"type": "bar",
"x": ["Product A", "Product B", "Product C", "Product D"],
"y": [1200, 950, 800, 650],
"marker": {"color": "#44BAB8"},
"text": ["$1,200", "$950", "$800", "$650"],
"textposition": "outside",
"textfont": {"size": 11, "color": "black"}
}],
"layout": {
"title": {
"text": "Revenue by Product",
"x": 0.5,
"font": {"size": 18, "color": "#2E41A6"}
},
"height": 500,
"showlegend": false,
"xaxis": {
"title": {"text": "Products", "font": {"size": 14, "color": "#2E41A6"}}
},
"yaxis": {
"title": {"text": "Revenue ($)", "font": {"size": 14, "color": "#2E41A6"}}
},
"margin": {"t": 80, "b": 80, "l": 80, "r": 80},
"font": {"family": "Arial", "size": 12},
"plot_bgcolor": "white",
"paper_bgcolor": "white"
}
}Key Points:
showlegend: false){
"data": [{
"type": "heatmap",
"x": ["Channel A", "Channel B", "Channel C"],
"y": ["Week 1", "Week 2", "Week 3"],
"z": [
[23.4, 45.6, 12.3],
[34.5, 56.7, 23.1],
[45.2, 67.8, 34.5]
],
"colorscale": [
[0, "#DAF1F1"],
[0.5, "#8FD6D4"],
[1, "#44BAB8"]
],
"showscale": true,
"colorbar": {
"title": {"text": "Performance", "font": {"size": 12}},
"titleside": "right"
},
"text": [
[23.4, 45.6, 12.3],
[34.5, 56.7, 23.1],
[45.2, 67.8, 34.5]
],
"texttemplate": "%{text:.1f}",
"textfont": {"size": 12, "color": "black"},
"hovertemplate": "<b>%{y}</b> - <b>%{x}</b><br>Value: %{z:.1f}<extra></extra>"
}],
"layout": {
"title": {
"text": "Performance Heatmap",
"x": 0.5,
"font": {"size": 18, "color": "#2E41A6"}
},
"height": 500,
"xaxis": {
"title": {"text": "Channels", "font": {"size": 14, "color": "#2E41A6"}}
},
"yaxis": {
"title": {"text": "Time Periods", "font": {"size": 14, "color": "#2E41A6"}}
},
"margin": {"t": 80, "b": 80, "l": 80, "r": 100},
"font": {"family": "Arial", "size": 12},
"plot_bgcolor": "white",
"paper_bgcolor": "white"
}
}Key Points:
text array matching z valuestexttemplate: "%{text:.1f}" for 1 decimal formatting{
"data": [
{
"type": "scatter",
"mode": "lines+markers",
"x": ["Jan", "Feb", "Mar", "Apr"],
"y": [10, 15, 20, 25],
"name": "Channel A",
"line": {"color": "#44BAB8", "width": 3},
"marker": {"color": "#44BAB8", "size": 8}
},
{
"type": "scatter",
"mode": "lines+markers",
"x": ["Jan", "Feb", "Mar", "Apr"],
"y": [8, 12, 18, 22],
"name": "Channel B",
"line": {"color": "#8FD6D4", "width": 3},
"marker": {"color": "#8FD6D4", "size": 8}
}
],
"layout": {
"title": {
"text": "Performance Trends",
"x": 0.5,
"font": {"size": 18, "color": "#2E41A6"}
},
"height": 500,
"showlegend": true,
"legend": {
"orientation": "h",
"yanchor": "bottom",
"y": 1.02,
"xanchor": "center",
"x": 0.5,
"font": {"size": 12}
},
"xaxis": {
"title": {"text": "Time Period", "font": {"size": 14, "color": "#2E41A6"}}
},
"yaxis": {
"title": {"text": "Performance", "font": {"size": 14, "color": "#2E41A6"}}
},
"margin": {"t": 100, "b": 80, "l": 80, "r": 80},
"font": {"family": "Arial", "size": 12},
"plot_bgcolor": "white",
"paper_bgcolor": "white"
}
}Key Points:
scatter type with mode: "lines+markers"{
"data": [{
"type": "sankey",
"orientation": "h",
"node": {
"pad": 15,
"thickness": 30,
"line": {"color": "black", "width": 0.5},
"label": ["Source A", "Source B", "Destination X", "Destination Y"],
"color": ["#44BAB8", "#8FD6D4", "#2E41A6", "#828DCA"]
},
"link": {
"source": [0, 1, 0, 1],
"target": [2, 2, 3, 3],
"value": [10, 20, 15, 25],
"color": [
"rgba(68, 186, 184, 0.4)",
"rgba(143, 214, 212, 0.4)",
"rgba(68, 186, 184, 0.4)",
"rgba(143, 214, 212, 0.4)"
]
}
}],
"layout": {
"title": {
"text": "Customer Journey Flow",
"x": 0.5,
"font": {"size": 18, "color": "#2E41A6"}
},
"height": 600,
"margin": {"t": 80, "b": 50, "l": 50, "r": 50},
"font": {"family": "Arial", "size": 12},
"paper_bgcolor": "white"
}
}Key Points:
| Chart Type | Show Legend? | Position |
|---|---|---|
| Pie Chart | ✅ ALWAYS | Vertical, right side |
| Multi-Series Bar | ✅ ALWAYS | Horizontal, top |
| Single Bar | ❌ NEVER | N/A |
| Multi-Series Line | ✅ ALWAYS | Horizontal, top |
| Single Line | ❌ NEVER | N/A |
| Heatmap | ❌ (Use colorbar) | N/A |
| Sankey | ❌ (Labels in nodes) | N/A |
{
"showlegend": true,
"legend": {
"orientation": "v",
"yanchor": "middle",
"y": 0.5,
"xanchor": "left",
"x": 1.02,
"font": {"size": 12}
},
"margin": {"t": 80, "b": 80, "l": 80, "r": 150}
}{
"showlegend": true,
"legend": {
"orientation": "h",
"yanchor": "bottom",
"y": 1.05,
"xanchor": "center",
"x": 0.5,
"font": {"size": 12}
},
"margin": {"t": 120, "b": 80, "l": 80, "r": 80}
}// Option 1: Add % in template
{
"text": [45.2, 30.1, 24.7],
"texttemplate": "%{text}%",
"textposition": "outside",
"textfont": {"size": 11, "color": "black"}
}
// Option 2: Pre-formatted strings
{
"text": ["45.2%", "30.1%", "24.7%"],
"textposition": "outside",
"textfont": {"size": 11, "color": "black"}
}// Option 1: Format with template
{
"text": [1200000, 850000, 650000],
"texttemplate": "$%{text:,.0f}",
"textposition": "outside",
"textfont": {"size": 11, "color": "black"}
}
// Option 2: Pre-formatted strings
{
"text": ["$1.2M", "$850K", "$650K"],
"textposition": "outside",
"textfont": {"size": 11, "color": "black"}
}{
"text": [[23.4, 45.6], [34.5, 56.7]],
"texttemplate": "%{text:.1f}",
"textfont": {"size": 12, "color": "black"},
"hovertemplate": "Value: %{z:.1f}<extra></extra>"
}KPI indicators are the ONLY exception where subplots are allowed. Use simple number indicators only.
{
"data": [
{
"type": "indicator",
"mode": "number+delta",
"value": 5240,
"delta": {"reference": 4800, "suffix": " customers"},
"title": {"text": "Total Customers", "font": {"size": 12, "color": "#2E41A6"}},
"number": {"font": {"size": 32, "color": "#44BAB8"}},
"domain": {"x": [0, 0.25], "y": [0, 1]}
},
{
"type": "indicator",
"mode": "number+delta",
"value": 42.5,
"delta": {"reference": 38.2, "suffix": "%"},
"title": {"text": "Conversion Rate", "font": {"size": 12, "color": "#2E41A6"}},
"number": {"prefix": "", "suffix": "%", "font": {"size": 32, "color": "#44BAB8"}},
"domain": {"x": [0.25, 0.5], "y": [0, 1]}
},
{
"type": "indicator",
"mode": "number+delta",
"value": 1250000,
"delta": {"reference": 1100000, "valueformat": "$,.0f"},
"title": {"text": "Total Revenue", "font": {"size": 12, "color": "#2E41A6"}},
"number": {"prefix": "$", "valueformat": ",.0f", "font": {"size": 32, "color": "#44BAB8"}},
"domain": {"x": [0.5, 0.75], "y": [0, 1]}
},
{
"type": "indicator",
"mode": "number+delta",
"value": 25.3,
"delta": {"reference": 22.1, "suffix": "%"},
"title": {"text": "Champions %", "font": {"size": 12, "color": "#2E41A6"}},
"number": {"suffix": "%", "font": {"size": 32, "color": "#44BAB8"}},
"domain": {"x": [0.75, 1], "y": [0, 1]}
}
],
"layout": {
"height": 150,
"margin": {"t": 20, "b": 20, "l": 20, "r": 20},
"paper_bgcolor": "white"
}
}Key Points for KPI Indicators:
[0, 0.25], [0.25, 0.5], [0.5, 0.75], [0.75, 1]Before generating any visualization, verify:
"x": 0.5)"font": {"size": 14})"plot_bgcolor": "white", "paper_bgcolor": "white")"showlegend": true for pie charts and comparisons"text" and "textposition""textfont": {"color": "black"})yaxis2, xaxis2, domain specifications)
"data": "[...]")"y": -0.3)"text" property on bar charts - numbers won't show"textposition" on bar charts - numbers won't be positioned"texttemplate" on heatmaps - numbers won't be formatted"showscale" on heatmaps - color scale won't appear"t": 80 insufficient, use "t": 120)"y": 1.05 for horizontal, adequate margins"text": [...], "textposition": "outside""text": [...], "texttemplate": "%{text:.1f}""t": 120 minimum for legends"textfont": {"color": "black"}{
"data": [{
"type": "pie",
"values": [1250, 2100, 1500, 390],
"labels": ["Champions", "Loyal Customers", "At Risk", "Lost"],
"marker": {
"colors": ["#44BAB8", "#8FD6D4", "#EEB53A", "#828DCA"]
},
"textinfo": "label+percent",
"textposition": "auto",
"textfont": {"size": 14, "color": "black"},
"hovertemplate": "<b>%{label}</b><br>Customers: %{value}<br>Percentage: %{percent}<extra></extra>"
}],
"layout": {
"title": {
"text": "Customer Segment Distribution",
"x": 0.5,
"font": {"size": 18, "color": "#2E41A6"}
},
"height": 500,
"showlegend": true,
"legend": {
"orientation": "v",
"yanchor": "middle",
"y": 0.5,
"xanchor": "left",
"x": 1.02,
"font": {"size": 12}
},
"margin": {"t": 80, "b": 80, "l": 80, "r": 150},
"font": {"family": "Arial", "size": 12},
"paper_bgcolor": "white"
}
}{
"data": [
{
"type": "bar",
"x": ["Email", "Social", "Search", "Display"],
"y": [125000, 98000, 156000, 67000],
"name": "Q3 2024",
"marker": {"color": "#44BAB8"},
"text": ["$125K", "$98K", "$156K", "$67K"],
"textposition": "outside",
"textfont": {"size": 11, "color": "black"}
},
{
"type": "bar",
"x": ["Email", "Social", "Search", "Display"],
"y": [142000, 115000, 178000, 73000],
"name": "Q4 2024",
"marker": {"color": "#8FD6D4"},
"text": ["$142K", "$115K", "$178K", "$73K"],
"textposition": "outside",
"textfont": {"size": 11, "color": "black"}
}
],
"layout": {
"title": {
"text": "Revenue by Marketing Channel",
"x": 0.5,
"font": {"size": 18, "color": "#2E41A6"}
},
"height": 500,
"barmode": "group",
"showlegend": true,
"legend": {
"orientation": "h",
"yanchor": "bottom",
"y": 1.05,
"xanchor": "center",
"x": 0.5,
"font": {"size": 12}
},
"xaxis": {
"title": {"text": "Marketing Channels", "font": {"size": 14, "color": "#2E41A6"}}
},
"yaxis": {
"title": {"text": "Revenue ($)", "font": {"size": 14, "color": "#2E41A6"}}
},
"margin": {"t": 120, "b": 80, "l": 80, "r": 80},
"font": {"family": "Arial", "size": 12},
"plot_bgcolor": "white",
"paper_bgcolor": "white"
}
}{
"data": [{
"type": "heatmap",
"x": ["Week 1", "Week 2", "Week 3", "Week 4"],
"y": ["Email", "Social", "Search", "Display"],
"z": [
[85.3, 92.1, 88.7, 94.2],
[72.5, 78.3, 81.2, 76.8],
[91.4, 89.6, 93.8, 95.1],
[68.2, 71.5, 69.9, 73.4]
],
"colorscale": [
[0, "#DAF1F1"],
[0.5, "#8FD6D4"],
[1, "#44BAB8"]
],
"showscale": true,
"colorbar": {
"title": {"text": "Performance %", "font": {"size": 12}},
"titleside": "right"
},
"text": [
[85.3, 92.1, 88.7, 94.2],
[72.5, 78.3, 81.2, 76.8],
[91.4, 89.6, 93.8, 95.1],
[68.2, 71.5, 69.9, 73.4]
],
"texttemplate": "%{text:.1f}",
"textfont": {"size": 12, "color": "black"},
"hovertemplate": "<b>%{y}</b> - <b>%{x}</b><br>Performance: %{z:.1f}%<extra></extra>"
}],
"layout": {
"title": {
"text": "Weekly Channel Performance",
"x": 0.5,
"font": {"size": 18, "color": "#2E41A6"}
},
"height": 500,
"xaxis": {
"title": {"text": "Week", "font": {"size": 14, "color": "#2E41A6"}}
},
"yaxis": {
"title": {"text": "Channel", "font": {"size": 14, "color": "#2E41A6"}}
},
"margin": {"t": 80, "b": 80, "l": 100, "r": 100},
"font": {"family": "Arial", "size": 12},
"plot_bgcolor": "white",
"paper_bgcolor": "white"
}
}Your visualization is ready when:
✅ Executives can instantly understand the key message ✅ All text is readable at standard screen sizes ✅ Colors are consistent with TD branding ✅ Legends are visible and descriptive (where needed) ✅ Numbers are formatted appropriately ✅ Chart tells a clear story ✅ JSON structure is valid (not stringified) ✅ No subplots for analysis charts ✅ Clean, professional, executive-ready appearance
By following these guidelines, your Field Agent visualizations will be professional, consistent, and immediately actionable for decision-makers.
1a0845f
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.