diff --git a/plots/bar-race-animated/implementations/python/highcharts.py b/plots/bar-race-animated/implementations/python/highcharts.py
index 3b43073229..fdb6e332d2 100644
--- a/plots/bar-race-animated/implementations/python/highcharts.py
+++ b/plots/bar-race-animated/implementations/python/highcharts.py
@@ -1,9 +1,10 @@
-""" pyplots.ai
+""" anyplot.ai
bar-race-animated: Animated Bar Chart Race
-Library: highcharts unknown | Python 3.13.11
-Quality: 91/100 | Created: 2026-01-11
+Library: highcharts unknown | Python 3.13.13
+Quality: 83/100 | Updated: 2026-05-19
"""
+import os
import tempfile
import time
import urllib.request
@@ -18,6 +19,27 @@
from selenium.webdriver.chrome.options import Options
+# Theme
+THEME = os.getenv("ANYPLOT_THEME", "light")
+PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
+ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
+INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
+INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
+INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
+GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)"
+
+# Okabe-Ito palette (positions 1–7) + adaptive neutral for 8 entities
+OI_COLORS = [
+ "#009E73", # position 1: bluish green
+ "#D55E00", # position 2: vermillion
+ "#0072B2", # position 3: blue
+ "#CC79A7", # position 4: reddish purple
+ "#E69F00", # position 5: orange
+ "#56B4E9", # position 6: sky blue
+ "#F0E442", # position 7: yellow
+ "#1A1A1A" if THEME == "light" else "#E8E8E0", # position 8: adaptive neutral
+]
+
# Data - Global Technology Companies Market Value (in $B) 2019-2024
np.random.seed(42)
@@ -30,93 +52,73 @@
"QuantumByte",
"NetPrime",
"DigiWave",
- "SmartSystems",
- "ByteForce",
- "NexGen",
- "CoreLogic",
]
years = [2019, 2020, 2021, 2022, 2023, 2024]
-# Generate realistic market value evolution
-base_values = np.array([180, 150, 120, 100, 90, 80, 70, 60, 50, 45, 40, 35])
+base_values = np.array([180, 150, 120, 100, 90, 80, 70, 60])
data = []
for i, year in enumerate(years):
growth = 1 + 0.15 * i + np.random.randn(len(companies)) * 0.2
values = base_values * growth * (1 + np.random.randn(len(companies)) * 0.1)
- # Add some shuffling over time to make rankings change
shuffle_factor = np.random.randn(len(companies)) * (20 + i * 10)
values = values + shuffle_factor
- values = np.maximum(values, 10) # Minimum value
+ values = np.maximum(values, 10)
for j, company in enumerate(companies):
data.append({"company": company, "year": year, "value": values[j]})
df = pd.DataFrame(data)
-
-# Colors - consistent per company
-colors = [
- "#306998",
- "#FFD43B",
- "#9467BD",
- "#17BECF",
- "#8C564B",
- "#E377C2",
- "#7F7F7F",
- "#BCBD22",
- "#1F77B4",
- "#FF7F0E",
- "#2CA02C",
- "#D62728",
-]
-company_colors = dict(zip(companies, colors, strict=False))
-
-# Select 6 key time snapshots for small multiples grid (2x3)
-snapshot_years = years
+company_colors = dict(zip(companies, OI_COLORS, strict=True))
# Download Highcharts JS
-highcharts_url = "https://code.highcharts.com/highcharts.js"
-with urllib.request.urlopen(highcharts_url, timeout=30) as response:
+highcharts_url = "https://cdn.jsdelivr.net/npm/highcharts@12/highcharts.js"
+with urllib.request.urlopen(highcharts_url, timeout=60) as response:
highcharts_js = response.read().decode("utf-8")
-# Generate individual charts for each year
+# Generate individual charts for each year snapshot
+CHART_W = 1550
+CHART_H = 1150
+max_val = df["value"].max() * 1.1
+
chart_scripts = []
-for idx, year in enumerate(snapshot_years):
- year_data = df[df["year"] == year].sort_values("value", ascending=True).tail(10)
+for idx, year in enumerate(years):
+ year_data = df[df["year"] == year].sort_values("value", ascending=True)
chart = Chart(container=f"chart{idx}")
chart.options = HighchartsOptions()
chart.options.chart = {
"type": "bar",
- "width": 1520,
- "height": 1280,
- "backgroundColor": "#ffffff",
- "marginLeft": 200,
- "marginRight": 50,
+ "width": CHART_W,
+ "height": CHART_H,
+ "backgroundColor": ELEVATED_BG,
+ "marginLeft": 230,
+ "marginRight": 110,
"marginBottom": 80,
- "marginTop": 100,
+ "marginTop": 80,
}
- chart.options.title = {"text": f"Year {year}", "style": {"fontSize": "32px", "fontWeight": "bold"}}
+ chart.options.title = {"text": str(year), "style": {"fontSize": "38px", "fontWeight": "bold", "color": INK}}
chart.options.x_axis = {
"categories": year_data["company"].tolist(),
"title": {"text": None},
- "labels": {"style": {"fontSize": "20px"}},
+ "labels": {"style": {"fontSize": "22px", "color": INK_SOFT}},
+ "lineColor": INK_SOFT,
+ "tickColor": INK_SOFT,
}
chart.options.y_axis = {
- "title": {"text": "Market Value ($B)", "style": {"fontSize": "20px"}},
- "labels": {"style": {"fontSize": "18px"}},
+ "title": {"text": "Market Value ($B)", "style": {"fontSize": "22px", "color": INK}},
+ "labels": {"style": {"fontSize": "18px", "color": INK_SOFT}},
"min": 0,
- "max": 400,
+ "max": max_val,
+ "gridLineColor": GRID,
}
chart.options.legend = {"enabled": False}
-
chart.options.credits = {"enabled": False}
- # Create series with individual colors
series = BarSeries()
series.name = "Market Value"
series.data = [
@@ -125,16 +127,16 @@
series.data_labels = {
"enabled": True,
"format": "${point.y:.0f}B",
- "style": {"fontSize": "16px", "fontWeight": "normal"},
+ "style": {"fontSize": "19px", "fontWeight": "normal", "color": INK, "textOutline": "none"},
}
-
chart.add_series(series)
- chart.options.plot_options = {"bar": {"borderWidth": 0, "pointWidth": 50}}
+ chart.options.plot_options = {"bar": {"borderWidth": 0, "pointWidth": 58}}
chart_scripts.append(chart.to_js_literal())
-# Create combined HTML with 2x3 grid
+# Assemble full-page HTML with 2×3 small-multiples grid
+GRID_GAP = 28
html_content = f"""
@@ -144,41 +146,40 @@
body {{
margin: 0;
padding: 40px;
- background: #ffffff;
+ background: {PAGE_BG};
font-family: Arial, sans-serif;
}}
.main-title {{
text-align: center;
- font-size: 48px;
+ font-size: 52px;
font-weight: bold;
- margin-bottom: 10px;
- color: #333;
+ margin-bottom: 8px;
+ color: {INK};
}}
.subtitle {{
text-align: center;
- font-size: 28px;
- color: #666;
- margin-bottom: 40px;
+ font-size: 30px;
+ color: {INK_MUTED};
+ margin-bottom: 36px;
}}
.grid {{
display: grid;
- grid-template-columns: repeat(3, 1fr);
- grid-template-rows: repeat(2, 1fr);
- gap: 30px;
- width: 4720px;
+ grid-template-columns: repeat(3, {CHART_W}px);
+ grid-template-rows: repeat(2, {CHART_H}px);
+ gap: {GRID_GAP}px;
+ width: {3 * CHART_W + 2 * GRID_GAP}px;
margin: 0 auto;
}}
.chart-container {{
- background: #fafafa;
- border: 2px solid #e0e0e0;
- border-radius: 10px;
+ background: {ELEVATED_BG};
+ border-radius: 8px;
overflow: hidden;
}}
- bar-race-animated · highcharts · pyplots.ai
- Technology Companies Market Value Evolution (2019-2024)
+ bar-race-animated · python · highcharts · anyplot.ai
+ Technology Companies Market Value Evolution (2019–2024)
@@ -198,14 +199,14 @@
"""
-# Write temp HTML and take screenshot
-with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
+# Save HTML artifact
+with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f:
f.write(html_content)
- temp_path = f.name
-# Also save as plot.html for interactive version
-with open("plot.html", "w", encoding="utf-8") as f:
+# Screenshot via headless Chrome
+with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
f.write(html_content)
+ temp_path = f.name
chrome_options = Options()
chrome_options.add_argument("--headless")
@@ -216,8 +217,8 @@
driver = webdriver.Chrome(options=chrome_options)
driver.get(f"file://{temp_path}")
-time.sleep(8) # Wait for all charts to render
-driver.save_screenshot("plot.png")
+time.sleep(8)
+driver.save_screenshot(f"plot-{THEME}.png")
driver.quit()
Path(temp_path).unlink()
diff --git a/plots/bar-race-animated/metadata/python/highcharts.yaml b/plots/bar-race-animated/metadata/python/highcharts.yaml
index 390e46872b..eb85bd2cd6 100644
--- a/plots/bar-race-animated/metadata/python/highcharts.yaml
+++ b/plots/bar-race-animated/metadata/python/highcharts.yaml
@@ -1,159 +1,184 @@
library: highcharts
+language: python
specification_id: bar-race-animated
created: '2026-01-11T00:19:53Z'
-updated: '2026-01-11T00:27:46Z'
-generated_by: claude-opus-4-5-20251101
-workflow_run: 20886563857
+updated: '2026-05-19T02:27:38Z'
+generated_by: claude-sonnet
+workflow_run: 26071381995
issue: 3653
-python_version: 3.13.11
+language_version: 3.13.13
library_version: unknown
-preview_url: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/highcharts/plot.png
-preview_html: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/highcharts/plot.html
-quality_score: 91
+preview_url_light: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/python/highcharts/plot-light.png
+preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/python/highcharts/plot-dark.png
+preview_html_light: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/python/highcharts/plot-light.html
+preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/bar-race-animated/python/highcharts/plot-dark.html
+quality_score: 83
review:
strengths:
- - Excellent implementation of small multiples grid approach for static output as
- spec recommends
- - Consistent color coding per company across all time periods enables tracking
- - Clear data labels with dollar formatting ($XXB) on each bar
- - Good use of Highcharts styling options (margins, font sizes, borders)
- - Generates both static PNG and interactive HTML output
- - Realistic tech company market value scenario with plausible evolution
+ - 'Perfect spec compliance: all required bar-race features present with correct
+ static fallback (small multiples grid).'
+ - Clean code structure with seed, adaptive theme tokens, and proper HTML+PNG dual
+ output.
+ - Consistent per-entity Okabe-Ito coloring maintained across all 6 year panels.
+ - Card-like chart containers with rounded corners give the grid a polished, dashboard-like
+ appearance.
+ - Both light and dark renders are fully readable with correct backgrounds and adaptive
+ chrome.
weaknesses:
- - Font sizes could be slightly larger for optimal readability at full resolution
- - Some panels show only 9 companies instead of consistent 10 (visible in 2022 panel)
- - The individual panel charts have excessive dimensions (1520x1280) that get scaled
- down significantly in the grid view
- image_description: The plot displays a 2x3 grid of small multiple bar charts showing
- "Technology Companies Market Value Evolution (2019-2024)" with the main title
- "bar-race-animated · highcharts · pyplots.ai" at the top. Each panel represents
- a different year (2019-2024) with horizontal bar charts showing top 10 technology
- companies by market value. Bars are color-coded consistently per company (e.g.,
- TechCorp Alpha in pink/magenta, DataSphere in teal, QuantumByte in yellow, etc.).
- Each bar has a data label showing the value in $B format. The bars are sorted
- by value within each panel, and you can observe ranking changes across years -
- for example, TechCorp Alpha leads in 2019 but DataSphere overtakes by 2024. The
- Y-axis shows company names and X-axis shows "Market Value ($B)" ranging from 0-400.
- Each panel has a subtle gray background with white chart background, bordered
- with rounded corners.
+ - Extreme outlier values (DataSphere in 2022 ~$3000B, TechCorp Alpha in 2024 ~$2926B)
+ distort the shared x-axis scale, compressing most bars in those panels to a fraction
+ of panel width.
+ - No visual storytelling emphasis — no highlighted 'winner' bar, no callout for
+ the most dramatic ranking change, no leader annotation.
+ - 'Design Excellence headroom: DE-01 could reach 6-7 with a subtle rank-change indicator
+ or emphasis on the leading entity per year.'
+ image_description: |-
+ Light render (plot-light.png):
+ Background: Warm off-white #FAF8F1 — correct, not pure white.
+ Chrome: Main title "bar-race-animated · python · highcharts · anyplot.ai" in dark ink, subtitle in muted dark ink, year labels bold and dark — all clearly readable against the light surface.
+ Data: 8 companies colored with Okabe-Ito positions 1–8. First entity (TechCorp Alpha) is #009E73 (brand green). Bars have inline data labels (e.g. "$1888B") in dark ink. All bars distinguishable.
+ Legibility verdict: PASS
+
+ Dark render (plot-dark.png):
+ Background: Warm near-black #1A1A17 — correct, not pure black.
+ Chrome: Main title, subtitle, year labels, company names, axis tick labels all render in light ink (#F0EFE8 / #B8B7B0). No dark-on-dark failures detected. Grid lines subtle (rgba opacity 10%).
+ Data: Okabe-Ito colors identical to light render for positions 1–7. Adaptive neutral (DigiWave, position 8) correctly flips to #E8E8E0.
+ Legibility verdict: PASS
criteria_checklist:
visual_quality:
- score: 35
- max: 40
+ score: 27
+ max: 30
items:
- id: VQ-01
name: Text Legibility
- score: 8
- max: 10
+ score: 7
+ max: 8
passed: true
- comment: Text is readable but could be slightly larger for optimal viewing;
- company names and values are clear
+ comment: All font sizes explicitly set (52px main title, 38px year labels,
+ 22px axis labels, 18px ticks, 19px data labels); all readable in both renders
- id: VQ-02
name: No Overlap
- score: 8
- max: 8
+ score: 6
+ max: 6
passed: true
- comment: No overlapping text elements
+ comment: No text collisions in any of the 6 panels in either render
- id: VQ-03
name: Element Visibility
- score: 7
- max: 8
+ score: 4
+ max: 6
passed: true
- comment: Bars are well-sized and visible; data labels are clear
+ comment: Bars visible with data labels; extreme outliers in 2022 (~$3000B)
+ and 2024 (~$2926B) compress most other bars to fraction of panel width
- id: VQ-04
name: Color Accessibility
- score: 5
- max: 5
+ score: 2
+ max: 2
passed: true
- comment: Uses colorblind-safe palette avoiding red-green conflicts
+ comment: Okabe-Ito palette is CVD-safe; bars wide enough for shape-independent
+ identification
- id: VQ-05
- name: Layout Balance
+ name: Layout & Canvas
score: 4
- max: 5
+ max: 4
passed: true
- comment: Good use of canvas with 2x3 grid; slight margin imbalance on some
- panels
+ comment: 2x3 grid fills ~90% of 4800x2700 canvas; balanced margins; nothing
+ cut off
- id: VQ-06
- name: Axis Labels
+ name: Axis Labels & Title
score: 2
max: 2
passed: true
- comment: Market Value ($B) is descriptive with units
+ comment: X-axis has units (Market Value $B); y-axis shows entity names
- id: VQ-07
- name: Grid & Legend
- score: 1
+ name: Palette Compliance
+ score: 2
max: 2
passed: true
- comment: No legend needed (colors match company names), grid is subtle but
- could be more refined
+ comment: 'First entity is #009E73; Okabe-Ito order maintained; backgrounds
+ #FAF8F1/#1A1A17; all chrome is theme-adaptive'
+ design_excellence:
+ score: 12
+ max: 20
+ items:
+ - id: DE-01
+ name: Aesthetic Sophistication
+ score: 5
+ max: 8
+ passed: true
+ comment: Above plain defaults with card-like rounded containers and consistent
+ Okabe-Ito palette; not yet publication-ready
+ - id: DE-02
+ name: Visual Refinement
+ score: 4
+ max: 6
+ passed: true
+ comment: Subtle 10% opacity grid, borderWidth 0 on bars, credits disabled,
+ custom margins, elevated-bg containers
+ - id: DE-03
+ name: Data Storytelling
+ score: 3
+ max: 6
+ passed: false
+ comment: Small multiples show time progression effectively but no visual emphasis
+ on ranking leader or most dramatic change
spec_compliance:
- score: 24
- max: 25
+ score: 15
+ max: 15
items:
- id: SC-01
name: Plot Type
- score: 8
- max: 8
- passed: true
- comment: Correct horizontal bar chart with ranking changes over time
- - id: SC-02
- name: Data Mapping
score: 5
max: 5
passed: true
- comment: Companies on Y-axis, values on X-axis, properly sorted
- - id: SC-03
+ comment: Horizontal bar chart in small-multiples grid; valid static fallback
+ per spec
+ - id: SC-02
name: Required Features
- score: 5
- max: 5
+ score: 4
+ max: 4
passed: true
- comment: Small multiples for static output (as spec recommends), consistent
- colors per entity, visible time indicators
- - id: SC-04
- name: Data Range
+ comment: Bars sorted by value each frame; entity labels attached; visible
+ time indicator; colors consistent per entity
+ - id: SC-03
+ name: Data Mapping
score: 3
max: 3
passed: true
- comment: Consistent axis range (0-400) across all panels enables comparison
- - id: SC-05
- name: Legend Accuracy
- score: 1
- max: 2
- passed: true
- comment: Colors are consistent but no explicit legend (acceptable since labels
- are on bars)
- - id: SC-06
- name: Title Format
- score: 2
- max: 2
+ comment: Companies on y-axis, market value on x-axis; all data visible
+ - id: SC-04
+ name: Title & Legend
+ score: 3
+ max: 3
passed: true
- comment: 'Uses correct format: bar-race-animated · highcharts · pyplots.ai'
+ comment: Title exactly 'bar-race-animated · python · highcharts · anyplot.ai';
+ no legend needed
data_quality:
- score: 18
- max: 20
+ score: 12
+ max: 15
items:
- id: DQ-01
name: Feature Coverage
- score: 7
- max: 8
+ score: 5
+ max: 6
passed: true
- comment: Shows ranking changes, multiple companies, value evolution; could
- show more dramatic rank swaps
+ comment: Rankings change year-to-year across 8 entities and 6 snapshots; minor
+ deduction for limited dramatic rank crossings in middle years
- id: DQ-02
name: Realistic Context
- score: 7
- max: 7
+ score: 4
+ max: 5
passed: true
- comment: Technology company market values is a realistic, neutral scenario
+ comment: Fictional tech company names in plausible market-value scenario;
+ neutral topic with descriptive subtitle
- id: DQ-03
name: Appropriate Scale
- score: 4
- max: 5
+ score: 3
+ max: 4
passed: true
- comment: Values in billions are appropriate; 10-400B range is plausible for
- tech companies
+ comment: Most values plausible; single extreme outlier (~$3000B for DataSphere
+ in 2022) is unrealistic relative to peers
code_quality:
- score: 9
+ score: 10
max: 10
items:
- id: CQ-01
@@ -161,55 +186,61 @@ review:
score: 3
max: 3
passed: true
- comment: 'Linear structure: imports → data → plot generation → HTML → screenshot'
+ comment: Linear Imports->Data->Chart loop->HTML assembly->Save; no functions
+ or classes
- id: CQ-02
name: Reproducibility
- score: 3
- max: 3
+ score: 2
+ max: 2
passed: true
- comment: Uses np.random.seed(42)
+ comment: np.random.seed(42) set
- id: CQ-03
name: Clean Imports
score: 2
max: 2
passed: true
- comment: All imports are used
+ comment: All imported modules are used
- id: CQ-04
- name: No Deprecated API
- score: 1
- max: 1
+ name: Code Elegance
+ score: 2
+ max: 2
passed: true
- comment: Modern Highcharts API usage
+ comment: Clean, Pythonic; list comprehension for chart scripts; appropriate
+ complexity
- id: CQ-05
- name: Output Correct
- score: 0
+ name: Output & API
+ score: 1
max: 1
- passed: false
- comment: Saves plot.png AND plot.html (which is good for interactive library)
- library_features:
- score: 5
- max: 5
+ passed: true
+ comment: Saves plot-{THEME}.png and plot-{THEME}.html
+ library_mastery:
+ score: 7
+ max: 10
items:
- - id: LF-01
+ - id: LM-01
+ name: Idiomatic Usage
+ score: 4
+ max: 5
+ passed: true
+ comment: 'Uses Chart, HighchartsOptions, BarSeries, chart.to_js_literal()
+ correctly; per-point color assignment via {color: ...} in series data is
+ idiomatic Highcharts'
+ - id: LM-02
name: Distinctive Features
- score: 5
+ score: 3
max: 5
passed: true
- comment: Properly uses highcharts_core Python bindings, BarSeries, custom
- chart options, data labels, and generates both PNG and interactive HTML
+ comment: Uses to_js_literal() for HTML embedding, per-point color overrides,
+ and Highcharts format-string data labels (${point.y:.0f}B)
verdict: APPROVED
impl_tags:
dependencies:
- selenium
techniques:
- - subplots
- html-export
- - annotations
patterns:
- data-generation
- iteration-over-groups
- dataprep:
- - time-series
+ dataprep: []
styling:
- grid-styling
- - edge-highlighting