From 9d40737eab5aa675bbd16d4165556987fa457768 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:38:04 +0000 Subject: [PATCH 1/5] feat(highcharts): implement slope-basic --- .../implementations/javascript/highcharts.js | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 plots/slope-basic/implementations/javascript/highcharts.js diff --git a/plots/slope-basic/implementations/javascript/highcharts.js b/plots/slope-basic/implementations/javascript/highcharts.js new file mode 100644 index 0000000000..0536520283 --- /dev/null +++ b/plots/slope-basic/implementations/javascript/highcharts.js @@ -0,0 +1,118 @@ +// anyplot.ai +// slope-basic: Basic Slope Chart (Slopegraph) +// Library: Highcharts 12.6.0 | Node 22 +// License: Highcharts — commercial license, free for non-commercial use (highcharts.com/license) +// Quality: pending | Created: 2026-07-25 + +const t = window.ANYPLOT_TOKENS; + +// --- Data (in-memory, deterministic) ---------------------------------------- +// Customer satisfaction score (0-100) for 10 products, two survey waves. +const products = [ + { name: "CloudSync", start: 62, end: 78 }, + { name: "DataVault", start: 88, end: 91 }, + { name: "PixelPro", start: 74, end: 65 }, + { name: "NetGuard", start: 55, end: 72 }, + { name: "FlowStream", start: 80, end: 76 }, + { name: "CoreLogic", start: 69, end: 84 }, + { name: "ByteWave", start: 91, end: 88 }, + { name: "GridPoint", start: 58, end: 61 }, + { name: "LinkHub", start: 77, end: 59 }, + { name: "TaskFlow", start: 66, end: 82 }, +]; + +const INCREASE = t.palette[0]; // brand green — profit/up/gain +const DECREASE = t.palette[4]; // matte red — loss/down +const FLAT = t.muted; + +const lineOf = (p) => (p.end > p.start ? INCREASE : p.end < p.start ? DECREASE : FLAT); + +// --- Chart ------------------------------------------------------------------- +Highcharts.chart("container", { + chart: { + type: "line", + backgroundColor: "transparent", + animation: false, + style: { fontFamily: "inherit" }, + marginLeft: 170, + marginRight: 170, + }, + credits: { enabled: false }, + title: { + text: "slope-basic · javascript · highcharts · anyplot.ai", + style: { color: t.ink, fontSize: "22px", fontWeight: "600" }, + }, + xAxis: { + categories: ["Survey Wave 1", "Survey Wave 2"], + lineColor: t.inkSoft, + tickColor: t.inkSoft, + gridLineWidth: 0, + minPadding: 0.08, + maxPadding: 0.08, + labels: { style: { color: t.inkSoft, fontSize: "14px" } }, + }, + yAxis: { + title: { + text: "Customer Satisfaction Score", + style: { color: t.inkSoft, fontSize: "16px" }, + }, + min: 40, + max: 100, + gridLineWidth: 0, + lineColor: t.inkSoft, + tickColor: t.inkSoft, + labels: { style: { color: t.inkSoft, fontSize: "14px" } }, + }, + legend: { + enabled: true, + align: "center", + verticalAlign: "bottom", + itemStyle: { color: t.inkSoft, fontSize: "14px", fontWeight: "normal" }, + itemHoverStyle: { color: t.ink }, + }, + plotOptions: { + series: { + animation: false, + enableMouseTracking: false, + marker: { enabled: true, radius: 5, symbol: "circle" }, + lineWidth: 2, + dataLabels: { + enabled: true, + crop: false, + overflow: "allow", + style: { fontSize: "14px", fontWeight: "normal", textOutline: "none" }, + }, + }, + }, + series: [ + // Legend-only entries — real product lines below are excluded from the legend + // since 10 individual names would overwhelm it; color already carries the story. + { name: "Improved", type: "line", color: INCREASE, data: [], marker: { enabled: true } }, + { name: "Declined", type: "line", color: DECREASE, data: [], marker: { enabled: true } }, + ...products.map((p) => ({ + name: p.name, + showInLegend: false, + color: lineOf(p), + data: [ + { + y: p.start, + dataLabels: { + align: "right", + x: -12, + color: t.inkSoft, + format: `${p.name} · ${p.start}`, + }, + }, + { + y: p.end, + dataLabels: { + align: "left", + x: 12, + color: t.inkSoft, + format: `${p.end} · ${p.name}`, + }, + }, + ], + })), + ], +}); From 471fc99f1d13a09c92727c4f5f68af0254d3e03a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:38:12 +0000 Subject: [PATCH 2/5] chore(highcharts): add metadata for slope-basic --- .../metadata/javascript/highcharts.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 plots/slope-basic/metadata/javascript/highcharts.yaml diff --git a/plots/slope-basic/metadata/javascript/highcharts.yaml b/plots/slope-basic/metadata/javascript/highcharts.yaml new file mode 100644 index 0000000000..c3648d77c5 --- /dev/null +++ b/plots/slope-basic/metadata/javascript/highcharts.yaml @@ -0,0 +1,21 @@ +# Per-library metadata for highcharts implementation of slope-basic +# Auto-generated by impl-generate.yml + +library: highcharts +language: javascript +specification_id: slope-basic +created: '2026-07-25T23:38:12Z' +updated: '2026-07-25T23:38:12Z' +generated_by: claude-sonnet +workflow_run: 30179585442 +issue: 981 +language_version: 22.23.1 +library_version: 12.6.0 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/highcharts/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/highcharts/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/highcharts/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/highcharts/plot-dark.html +quality_score: null +review: + strengths: [] + weaknesses: [] From 2c31bcc30405828cfe3f60dfd1d89a510380a0d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:44:04 +0000 Subject: [PATCH 3/5] chore(highcharts): update quality score 89 and review feedback for slope-basic --- .../implementations/javascript/highcharts.js | 5 +- .../metadata/javascript/highcharts.yaml | 248 +++++++++++++++++- 2 files changed, 243 insertions(+), 10 deletions(-) diff --git a/plots/slope-basic/implementations/javascript/highcharts.js b/plots/slope-basic/implementations/javascript/highcharts.js index 0536520283..b3558c35b5 100644 --- a/plots/slope-basic/implementations/javascript/highcharts.js +++ b/plots/slope-basic/implementations/javascript/highcharts.js @@ -1,8 +1,7 @@ // anyplot.ai // slope-basic: Basic Slope Chart (Slopegraph) -// Library: Highcharts 12.6.0 | Node 22 -// License: Highcharts — commercial license, free for non-commercial use (highcharts.com/license) -// Quality: pending | Created: 2026-07-25 +// Library: highcharts 12.6.0 | JavaScript 22.23.1 +// Quality: 89/100 | Created: 2026-07-25 const t = window.ANYPLOT_TOKENS; diff --git a/plots/slope-basic/metadata/javascript/highcharts.yaml b/plots/slope-basic/metadata/javascript/highcharts.yaml index c3648d77c5..6205b4be21 100644 --- a/plots/slope-basic/metadata/javascript/highcharts.yaml +++ b/plots/slope-basic/metadata/javascript/highcharts.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for highcharts implementation of slope-basic -# Auto-generated by impl-generate.yml - library: highcharts language: javascript specification_id: slope-basic created: '2026-07-25T23:38:12Z' -updated: '2026-07-25T23:38:12Z' +updated: '2026-07-25T23:44:03Z' generated_by: claude-sonnet workflow_run: 30179585442 issue: 981 @@ -15,7 +12,244 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/slope-bas preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/highcharts/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/highcharts/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/highcharts/plot-dark.html -quality_score: null +quality_score: 89 review: - strengths: [] - weaknesses: [] + strengths: + - 'Correct Imprint semantic color mapping: brand green (palette[0]) for "Improved", + matte-red semantic anchor (palette[4]) for "Declined" — matches the style guide''s + finance/sentiment semantic-exception rule' + - Direct dual-endpoint data labels (name + value at both Survey Wave 1 and Survey + Wave 2) satisfy the spec's "labels at both endpoints" requirement without relying + on hover + - 'Clean, minimal chrome: no gridlines, transparent background, theme-adaptive ink + tokens correctly threaded through title/axis/legend/dataLabels in both light and + dark renders' + - Phantom-series legend pattern (showInLegend:false on the 10 real product lines, + two empty dummy series for "Improved"/"Declined") avoids an unreadable 10-entry + legend while still explaining the color coding + - Color direction is redundantly encoded by each line's slope (up = improved, down + = declined), keeping the chart colorblind-safe despite the red/green semantic + choice + weaknesses: + - Y-axis is fixed to a 40-100 range while actual data spans only 55-91, leaving + roughly 30% of the vertical canvas as dead space above and below the data band + — tighten the domain (e.g. ~50-95) for better canvas utilization (VQ-05) + - A few endpoint label pairs sit close together vertically (e.g. '84 · CoreLogic' + / '82 · TaskFlow' and '78 · CloudSync' / '76 · FlowStream' on Survey Wave 2; the + 80/77/74 cluster on Survey Wave 1) — not clipped but tight; consider a small collision-nudge + or leader-line offset when two labels fall within ~3 score points of each other + (VQ-02) + - plotOptions.series.enableMouseTracking:false disables all native Highcharts hover/tooltip + interactivity for every series, discarding the interactive library's distinctive + value-add in the HTML detail view; since data is already fully labeled, consider + re-enabling tooltips instead of disabling tracking entirely (LM-02) + - No explicit callout/annotation on the standout mover (e.g. NetGuard +17 or LinkHub + -18) — a small highlight would sharpen the data-storytelling focal point (DE-03) + image_description: |- + Light render (plot-light.png): + Background: Warm off-white, consistent with #FAF8F1 — not pure white. + Chrome: Title "slope-basic · javascript · highcharts · anyplot.ai" bold dark text at top center; y-axis title "Customer Satisfaction Score" and tick labels (40-100) in soft dark gray on the left; x-axis category labels "Survey Wave 1" / "Survey Wave 2" below the two vertical lanes; legend ("Improved" / "Declined") centered at the bottom. All chrome text is dark and clearly legible against the light background. + Data: 10 product lines connect Wave 1 to Wave 2 values, each line colored brand green (#009E73, "Improved") or matte red ("Declined") depending on direction. Direct data labels showing "{name} · {value}" flank both endpoints of every line, right-aligned on the left column and left-aligned on the right column, in soft ink color. Markers are small filled circles at each endpoint. + Legibility verdict: PASS — every title, axis label, tick label, and data label is clearly readable; no light-on-light text found. + + Dark render (plot-dark.png): + Background: Warm near-black, consistent with #1A1A17 — not pure black. + Chrome: Same title, axis titles, tick labels, category labels, and legend, now rendered in light gray/off-white tones against the dark background. Layout is pixel-identical to the light render. + Data: The same 10 lines with identical brand-green and matte-red hues as the light render — data colors did not shift between themes, only the chrome (text, background) flipped as expected. + Legibility verdict: PASS — no dark-on-dark failures; all text (title, axis labels, tick labels, data labels, legend) is clearly visible against the near-black surface. + criteria_checklist: + visual_quality: + score: 28 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 8 + max: 8 + passed: true + comment: All font sizes explicitly set (title 22px, axis 16px, ticks/legend/dataLabels + 14px); no overflow; readable in both themes + - id: VQ-02 + name: No Overlap + score: 5 + max: 6 + passed: true + comment: No true overlap, but a few endpoint-label pairs sit tightly stacked + (2-3 score points apart) + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: Marker radius 5 / line width 2 well matched for 10 sparse lines + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Green/red direction encoding is redundant with each line's up/down + slope + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: true + comment: Fixed 40-100 y-domain leaves noticeable dead space above/below the + 55-91 data band + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: '"Customer Satisfaction Score" plus labeled survey waves are fully + descriptive' + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: Correct Imprint semantic assignment (green=improved pos.1, matte + red=declined pos.5); theme-correct chrome both renders + design_excellence: + score: 16 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: Thoughtful semantic color use, phantom-series legend, direct labeling + — clearly above a generic default line chart + - id: DE-02 + name: Visual Refinement + score: 5 + max: 6 + passed: true + comment: No gridlines, minimal chrome, generous whitespace; docked slightly + for label crowding + - id: DE-03 + name: Data Storytelling + score: 5 + max: 6 + passed: true + comment: Color + slope direction instantly conveys the up/down story; no explicit + callout on the largest mover + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct slope-chart construction via line series across two categorical + positions + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Endpoint labels, direction color-coding, and labeled time-point axes + all present + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X = survey wave, Y = satisfaction score; all 10 entities visible + on both axes + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title format exact; legend labels ("Improved"/"Declined") match the + data story + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Shows both increases and decreases across 10 entities + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Plausible, neutral business scenario (product satisfaction across + two survey waves) + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 0-100 satisfaction scores with realistic 55-91 spread + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: No functions/classes, linear data-then-chart structure + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Deterministic hardcoded data, no RNG needed + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: No stray imports; uses only the Highcharts global + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean, appropriate complexity, no fake UI + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Correct mount-node contract, animation disabled, credits disabled + library_mastery: + score: 5 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Idiomatic dataLabels + phantom-legend-series pattern, though enableMouseTracking:false + is an unusual choice for an interactive library + - id: LM-02 + name: Distinctive Features + score: 1 + max: 5 + passed: false + comment: Direction-coded line-with-labels approach is fairly generic and replicable + in other libraries; native interactivity was disabled rather than leveraged + verdict: REJECTED +impl_tags: + dependencies: [] + techniques: + - annotations + - custom-legend + patterns: + - iteration-over-groups + dataprep: [] + styling: + - minimal-chrome From 9979b7416465dc51585a5a1e6592be2c0eafe46b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:52:32 +0000 Subject: [PATCH 4/5] fix(highcharts): address review feedback for slope-basic Attempt 1/4 - fixes based on AI review --- .../implementations/javascript/highcharts.js | 103 +++++++++++++----- 1 file changed, 75 insertions(+), 28 deletions(-) diff --git a/plots/slope-basic/implementations/javascript/highcharts.js b/plots/slope-basic/implementations/javascript/highcharts.js index b3558c35b5..76d7d63f31 100644 --- a/plots/slope-basic/implementations/javascript/highcharts.js +++ b/plots/slope-basic/implementations/javascript/highcharts.js @@ -26,6 +26,38 @@ const FLAT = t.muted; const lineOf = (p) => (p.end > p.start ? INCREASE : p.end < p.start ? DECREASE : FLAT); +// Tighten the y-domain to the actual data band (55-91) instead of a fixed +// 40-100 range, so the canvas isn't dominated by dead space. +const scores = products.flatMap((p) => [p.start, p.end]); +const yPad = 5; +const yMin = Math.floor(Math.min(...scores) - yPad); +const yMax = Math.ceil(Math.max(...scores) + yPad); + +// The mover with the largest absolute change gets a slightly heavier line +// and marker so the standout swing reads at a glance. +const standout = products.reduce((a, p) => (Math.abs(p.end - p.start) > Math.abs(a.end - a.start) ? p : a)); + +// Endpoint labels within NUDGE_GAP score points of a neighbor at the same +// wave get nudged apart vertically (label only, marker stays put) so +// stacked text doesn't crowd together. +const NUDGE_GAP = 3; +const NUDGE_PX = 6; +const labelNudges = (values) => { + const byValue = [...values].sort((a, b) => a.value - b.value); + const nudge = new Map(values.map((v) => [v.name, 0])); + for (let i = 1; i < byValue.length; i++) { + const lo = byValue[i - 1]; + const hi = byValue[i]; + if (hi.value - lo.value <= NUDGE_GAP) { + nudge.set(lo.name, nudge.get(lo.name) + NUDGE_PX); + nudge.set(hi.name, nudge.get(hi.name) - NUDGE_PX); + } + } + return nudge; +}; +const startNudge = labelNudges(products.map((p) => ({ name: p.name, value: p.start }))); +const endNudge = labelNudges(products.map((p) => ({ name: p.name, value: p.end }))); + // --- Chart ------------------------------------------------------------------- Highcharts.chart("container", { chart: { @@ -55,8 +87,10 @@ Highcharts.chart("container", { text: "Customer Satisfaction Score", style: { color: t.inkSoft, fontSize: "16px" }, }, - min: 40, - max: 100, + min: yMin, + max: yMax, + startOnTick: false, + endOnTick: false, gridLineWidth: 0, lineColor: t.inkSoft, tickColor: t.inkSoft, @@ -69,10 +103,16 @@ Highcharts.chart("container", { itemStyle: { color: t.inkSoft, fontSize: "14px", fontWeight: "normal" }, itemHoverStyle: { color: t.ink }, }, + tooltip: { + headerFormat: "", + pointFormat: "{series.name}: {point.y}", + backgroundColor: t.elevatedBg, + borderColor: t.inkSoft, + style: { color: t.ink, fontSize: "13px" }, + }, plotOptions: { series: { animation: false, - enableMouseTracking: false, marker: { enabled: true, radius: 5, symbol: "circle" }, lineWidth: 2, dataLabels: { @@ -86,32 +126,39 @@ Highcharts.chart("container", { series: [ // Legend-only entries — real product lines below are excluded from the legend // since 10 individual names would overwhelm it; color already carries the story. - { name: "Improved", type: "line", color: INCREASE, data: [], marker: { enabled: true } }, - { name: "Declined", type: "line", color: DECREASE, data: [], marker: { enabled: true } }, - ...products.map((p) => ({ - name: p.name, - showInLegend: false, - color: lineOf(p), - data: [ - { - y: p.start, - dataLabels: { - align: "right", - x: -12, - color: t.inkSoft, - format: `${p.name} · ${p.start}`, + { name: "Improved", type: "line", color: INCREASE, data: [], marker: { enabled: true }, enableMouseTracking: false }, + { name: "Declined", type: "line", color: DECREASE, data: [], marker: { enabled: true }, enableMouseTracking: false }, + ...products.map((p) => { + const isStandout = p.name === standout.name; + return { + name: p.name, + showInLegend: false, + color: lineOf(p), + lineWidth: isStandout ? 3.5 : 2, + marker: { radius: isStandout ? 6.5 : 5 }, + data: [ + { + y: p.start, + dataLabels: { + align: "right", + x: -12, + y: startNudge.get(p.name), + color: t.inkSoft, + format: `${p.name} · ${p.start}`, + }, }, - }, - { - y: p.end, - dataLabels: { - align: "left", - x: 12, - color: t.inkSoft, - format: `${p.end} · ${p.name}`, + { + y: p.end, + dataLabels: { + align: "left", + x: 12, + y: endNudge.get(p.name), + color: t.inkSoft, + format: `${p.end} · ${p.name}`, + }, }, - }, - ], - })), + ], + }; + }), ], }); From d3acd623e0024a4de5b69fce44ae3aa5f082c40e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:58:39 +0000 Subject: [PATCH 5/5] chore(highcharts): update quality score 96 and review feedback for slope-basic --- .../implementations/javascript/highcharts.js | 2 +- .../metadata/javascript/highcharts.yaml | 142 +++++++++--------- 2 files changed, 75 insertions(+), 69 deletions(-) diff --git a/plots/slope-basic/implementations/javascript/highcharts.js b/plots/slope-basic/implementations/javascript/highcharts.js index 76d7d63f31..346c89fb9f 100644 --- a/plots/slope-basic/implementations/javascript/highcharts.js +++ b/plots/slope-basic/implementations/javascript/highcharts.js @@ -1,7 +1,7 @@ // anyplot.ai // slope-basic: Basic Slope Chart (Slopegraph) // Library: highcharts 12.6.0 | JavaScript 22.23.1 -// Quality: 89/100 | Created: 2026-07-25 +// Quality: 96/100 | Created: 2026-07-25 const t = window.ANYPLOT_TOKENS; diff --git a/plots/slope-basic/metadata/javascript/highcharts.yaml b/plots/slope-basic/metadata/javascript/highcharts.yaml index 6205b4be21..eec45c35e5 100644 --- a/plots/slope-basic/metadata/javascript/highcharts.yaml +++ b/plots/slope-basic/metadata/javascript/highcharts.yaml @@ -2,7 +2,7 @@ library: highcharts language: javascript specification_id: slope-basic created: '2026-07-25T23:38:12Z' -updated: '2026-07-25T23:44:03Z' +updated: '2026-07-25T23:58:38Z' generated_by: claude-sonnet workflow_run: 30179585442 issue: 981 @@ -12,54 +12,53 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/slope-bas preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/highcharts/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/highcharts/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/highcharts/plot-dark.html -quality_score: 89 +quality_score: 96 review: strengths: - - 'Correct Imprint semantic color mapping: brand green (palette[0]) for "Improved", - matte-red semantic anchor (palette[4]) for "Declined" — matches the style guide''s - finance/sentiment semantic-exception rule' - - Direct dual-endpoint data labels (name + value at both Survey Wave 1 and Survey - Wave 2) satisfy the spec's "labels at both endpoints" requirement without relying - on hover - - 'Clean, minimal chrome: no gridlines, transparent background, theme-adaptive ink - tokens correctly threaded through title/axis/legend/dataLabels in both light and - dark renders' - - Phantom-series legend pattern (showInLegend:false on the 10 real product lines, - two empty dummy series for "Improved"/"Declined") avoids an unreadable 10-entry - legend while still explaining the color coding - - Color direction is redundantly encoded by each line's slope (up = improved, down - = declined), keeping the chart colorblind-safe despite the red/green semantic - choice + - Y-axis domain is now tightened to the actual data band (~50-95 vs. the prior fixed + 40-100), eliminating the dead space flagged in the attempt-1 review and giving + the plot excellent canvas utilization + - 'Endpoint-label collision nudging (labelNudges helper) resolves the tight label + clusters from attempt 1 — verified pixel-level via cropped zooms on both the FlowStream/LinkHub/PixelPro + cluster and the CoreLogic/TaskFlow/CloudSync/FlowStream cluster: no overlap in + either theme' + - Real product series no longer set enableMouseTracking:false — native Highcharts + tooltips now work on the actual data, while the two phantom legend-only series + (empty data) still correctly use enableMouseTracking:false to avoid polluting + the legend without disabling interactivity where it matters + - The largest-magnitude mover (LinkHub, -18) is visually emphasized with a heavier + line (3.5 vs 2 lineWidth) and larger marker (6.5 vs 5 radius), giving the chart + a clear focal point without resorting to fake/simulated interactivity + - 'Correct Imprint semantic color mapping retained: brand green (palette[0]) for + improved, matte red (palette[4]) for declined — matches the style guide''s finance/sentiment + semantic-exception rule, and direction is redundantly encoded by slope, keeping + it colorblind-safe' + - All font sizes explicitly set (title 22px, axis titles 16px, ticks/legend/dataLabels + 14px), readable in both light and dark renders with no theme-adaptation failures weaknesses: - - Y-axis is fixed to a 40-100 range while actual data spans only 55-91, leaving - roughly 30% of the vertical canvas as dead space above and below the data band - — tighten the domain (e.g. ~50-95) for better canvas utilization (VQ-05) - - A few endpoint label pairs sit close together vertically (e.g. '84 · CoreLogic' - / '82 · TaskFlow' and '78 · CloudSync' / '76 · FlowStream' on Survey Wave 2; the - 80/77/74 cluster on Survey Wave 1) — not clipped but tight; consider a small collision-nudge - or leader-line offset when two labels fall within ~3 score points of each other - (VQ-02) - - plotOptions.series.enableMouseTracking:false disables all native Highcharts hover/tooltip - interactivity for every series, discarding the interactive library's distinctive - value-add in the HTML detail view; since data is already fully labeled, consider - re-enabling tooltips instead of disabling tracking entirely (LM-02) - - No explicit callout/annotation on the standout mover (e.g. NetGuard +17 or LinkHub - -18) — a small highlight would sharpen the data-storytelling focal point (DE-03) + - The standout-mover emphasis (thicker line/larger marker) is a good visual-hierarchy + device but is still not an explicit callout — a small text annotation or delta + badge (e.g. '-18') on the standout line would push the storytelling further toward + publication-grade + - 'Distinctive-feature usage is still moderate rather than showcase-level: the phantom-series + legend and dataLabels crop/overflow handling are genuinely Highcharts-specific, + but the chart otherwise reads as a fairly standard direct-labeled line chart that + other JS libraries could approximate with similar effort' image_description: |- Light render (plot-light.png): Background: Warm off-white, consistent with #FAF8F1 — not pure white. - Chrome: Title "slope-basic · javascript · highcharts · anyplot.ai" bold dark text at top center; y-axis title "Customer Satisfaction Score" and tick labels (40-100) in soft dark gray on the left; x-axis category labels "Survey Wave 1" / "Survey Wave 2" below the two vertical lanes; legend ("Improved" / "Declined") centered at the bottom. All chrome text is dark and clearly legible against the light background. - Data: 10 product lines connect Wave 1 to Wave 2 values, each line colored brand green (#009E73, "Improved") or matte red ("Declined") depending on direction. Direct data labels showing "{name} · {value}" flank both endpoints of every line, right-aligned on the left column and left-aligned on the right column, in soft ink color. Markers are small filled circles at each endpoint. - Legibility verdict: PASS — every title, axis label, tick label, and data label is clearly readable; no light-on-light text found. + Chrome: Title "slope-basic · javascript · highcharts · anyplot.ai" in bold dark text, top center. Y-axis title "Customer Satisfaction Score" (rotated) and tick labels (50-95, tightened to the actual data band) in soft dark gray on the left. X-axis category labels "Survey Wave 1" / "Survey Wave 2" below the two vertical lanes. Legend ("Improved" / "Declined") centered at the bottom. All chrome text is dark and clearly legible against the light background. + Data: 10 product lines connect Wave 1 to Wave 2 values, each colored brand green (#009E73, "Improved") or matte red ("Declined") depending on direction. The largest-magnitude mover (LinkHub, 77→59) renders with a visibly heavier line and larger marker than the rest, creating a clear focal point. Direct data labels showing "{name} · {value}" flank both endpoints of every line, right-aligned on the left column and left-aligned on the right column. Zoomed crops of the two closest label clusters (FlowStream/LinkHub/PixelPro on Wave 1; CoreLogic/TaskFlow/CloudSync/FlowStream on Wave 2) confirm no overlap — the collision-nudge logic keeps every label pair legibly separated. + Legibility verdict: PASS — every title, axis label, tick label, and data label is clearly readable; no light-on-light text found; no clipping at any canvas edge (verified via edge crops). Dark render (plot-dark.png): Background: Warm near-black, consistent with #1A1A17 — not pure black. Chrome: Same title, axis titles, tick labels, category labels, and legend, now rendered in light gray/off-white tones against the dark background. Layout is pixel-identical to the light render. - Data: The same 10 lines with identical brand-green and matte-red hues as the light render — data colors did not shift between themes, only the chrome (text, background) flipped as expected. - Legibility verdict: PASS — no dark-on-dark failures; all text (title, axis labels, tick labels, data labels, legend) is clearly visible against the near-black surface. + Data: The same 10 lines with identical brand-green and matte-red hues as the light render — data colors did not shift between themes, only chrome (text, background) flipped as expected. The LinkHub standout emphasis is equally visible in this theme. + Legibility verdict: PASS — no dark-on-dark failures; all text (title, axis labels, tick labels, data labels, legend) is clearly visible against the near-black surface, confirmed via the same cluster crops used for the light render. criteria_checklist: visual_quality: - score: 28 + score: 30 max: 30 items: - id: VQ-01 @@ -71,31 +70,33 @@ review: 14px); no overflow; readable in both themes - id: VQ-02 name: No Overlap - score: 5 + score: 6 max: 6 passed: true - comment: No true overlap, but a few endpoint-label pairs sit tightly stacked - (2-3 score points apart) + comment: Collision-nudge logic fixed the tight endpoint-label clusters flagged + in attempt 1 — verified via zoomed crops, no overlap remains - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: Marker radius 5 / line width 2 well matched for 10 sparse lines + comment: Marker radius 5 (6.5 standout) / line width 2 (3.5 standout) well + matched for 10 sparse lines - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true comment: Green/red direction encoding is redundant with each line's up/down - slope + slope, colorblind-safe - id: VQ-05 name: Layout & Canvas - score: 3 + score: 4 max: 4 passed: true - comment: Fixed 40-100 y-domain leaves noticeable dead space above/below the - 55-91 data band + comment: Y-domain now tightened to the actual 55-91 data band (padded ~50-95), + fixing the dead-space issue from attempt 1; balanced margins confirmed via + edge crops - id: VQ-06 name: Axis Labels & Title score: 2 @@ -109,9 +110,9 @@ review: max: 2 passed: true comment: Correct Imprint semantic assignment (green=improved pos.1, matte - red=declined pos.5); theme-correct chrome both renders + red=declined pos.5); theme-correct chrome in both renders design_excellence: - score: 16 + score: 18 max: 20 items: - id: DE-01 @@ -119,22 +120,23 @@ review: score: 6 max: 8 passed: true - comment: Thoughtful semantic color use, phantom-series legend, direct labeling - — clearly above a generic default line chart + comment: Thoughtful semantic color use, phantom-series legend, direct labeling, + standout-mover emphasis — clearly above a generic default line chart, though + short of FiveThirtyEight-level polish - id: DE-02 name: Visual Refinement - score: 5 + score: 6 max: 6 passed: true - comment: No gridlines, minimal chrome, generous whitespace; docked slightly - for label crowding + comment: No gridlines, minimal chrome, generous whitespace, and the label-crowding + weakness from attempt 1 is now resolved - id: DE-03 name: Data Storytelling - score: 5 + score: 6 max: 6 passed: true - comment: Color + slope direction instantly conveys the up/down story; no explicit - callout on the largest mover + comment: Color + slope direction plus the standout-mover line-weight/marker + emphasis gives the chart a clear, immediate focal point spec_compliance: score: 15 max: 15 @@ -176,7 +178,8 @@ review: score: 6 max: 6 passed: true - comment: Shows both increases and decreases across 10 entities + comment: Shows both increases and decreases with varying magnitude across + 10 entities - id: DQ-02 name: Realistic Context score: 5 @@ -199,7 +202,8 @@ review: score: 3 max: 3 passed: true - comment: No functions/classes, linear data-then-chart structure + comment: No functions/classes beyond small inline helpers, linear data-then-chart + structure - id: CQ-02 name: Reproducibility score: 2 @@ -211,13 +215,14 @@ review: score: 2 max: 2 passed: true - comment: No stray imports; uses only the Highcharts global + comment: No stray imports; uses only the Highcharts global and window tokens - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Clean, appropriate complexity, no fake UI + comment: Clean, appropriate complexity for the collision-nudge logic, no fake + UI - id: CQ-05 name: Output & API score: 1 @@ -225,24 +230,25 @@ review: passed: true comment: Correct mount-node contract, animation disabled, credits disabled library_mastery: - score: 5 + score: 8 max: 10 items: - id: LM-01 name: Idiomatic Usage - score: 4 + score: 5 max: 5 passed: true - comment: Idiomatic dataLabels + phantom-legend-series pattern, though enableMouseTracking:false - is an unusual choice for an interactive library + comment: Idiomatic dataLabels + phantom-legend-series pattern; enableMouseTracking:false + now correctly scoped only to the empty legend-proxy series - id: LM-02 name: Distinctive Features - score: 1 + score: 3 max: 5 - passed: false - comment: Direction-coded line-with-labels approach is fairly generic and replicable - in other libraries; native interactivity was disabled rather than leveraged - verdict: REJECTED + passed: true + comment: Tooltips re-enabled on real series, plus dataLabels crop:false/overflow:allow + and the phantom-legend pattern are genuinely Highcharts-specific techniques, + though the overall chart remains a fairly standard direct-labeled line chart + verdict: APPROVED impl_tags: dependencies: [] techniques: