Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions plots/dumbbell-basic/implementations/javascript/highcharts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// anyplot.ai
// dumbbell-basic: Basic Dumbbell Chart
// Library: highcharts 12.6.0 | JavaScript 22.23.0
// Quality: 89/100 | Created: 2026-06-30

//# anyplot-orientation: landscape

const t = window.ANYPLOT_TOKENS;

// Data — Employee satisfaction scores (0–10) before/after a workplace wellness initiative
const departments = ["Engineering", "Marketing", "Sales", "HR", "Finance", "Operations", "Legal", "Product"];
const scoreBefore = [6.2, 5.8, 6.5, 7.1, 5.4, 6.8, 6.0, 6.9];
const scoreAfter = [7.7, 7.4, 8.0, 8.3, 7.0, 8.2, 7.3, 8.4];

// Sort ascending by improvement so the largest improvement appears at top
const sortIdx = departments.map((_, i) => i)
.sort((a, b) => (scoreAfter[a] - scoreBefore[a]) - (scoreAfter[b] - scoreBefore[b]));
const labels = sortIdx.map(i => departments[i]);
const vBefore = sortIdx.map(i => scoreBefore[i]);
const vAfter = sortIdx.map(i => scoreAfter[i]);
const n = labels.length;

// [score, categoryIndex] pairs — x = score, y = integer row index
const afterData = vAfter.map((v, i) => [v, i]);
const beforeData = vBefore.map((v, i) => [v, i]);

const chart = Highcharts.chart("container", {
chart: {
type: "scatter",
backgroundColor: "transparent",
animation: false,
style: { fontFamily: "inherit" },
marginLeft: 160,
marginRight: 50,
marginTop: 90,
marginBottom: 70,
},
credits: { enabled: false },
colors: t.palette,
title: {
text: "dumbbell-basic · javascript · highcharts · anyplot.ai",
style: { color: t.ink, fontSize: "22px", fontWeight: "600" },
},
subtitle: {
text: "Employee satisfaction before/after workplace wellness initiative",
style: { color: t.inkSoft, fontSize: "14px" },
},
xAxis: {
title: {
text: "Satisfaction Score (0–10)",
style: { color: t.inkSoft, fontSize: "16px" },
},
lineColor: t.inkSoft,
tickColor: t.inkSoft,
gridLineColor: t.grid,
gridLineWidth: 1,
min: 4.5,
max: 9.5,
tickInterval: 1,
labels: { style: { color: t.inkSoft, fontSize: "14px" } },
},
yAxis: {
title: { text: null },
min: -0.5,
max: n - 0.5,
tickPositions: labels.map((_, i) => i),
labels: {
formatter() { return labels[this.value] || ""; },
style: { color: t.inkSoft, fontSize: "14px" },
},
lineColor: t.inkSoft,
tickColor: t.inkSoft,
gridLineColor: "transparent",
},
legend: {
verticalAlign: "top",
align: "right",
layout: "vertical",
itemStyle: { color: t.inkSoft, fontSize: "14px" },
itemHoverStyle: { color: t.ink },
},
plotOptions: {
series: { animation: false },
scatter: {
marker: { radius: 9, lineWidth: 0 },
},
},
series: [
{
name: "After",
data: afterData,
color: t.palette[0],
marker: { symbol: "circle" },
zIndex: 5,
},
{
name: "Before",
data: beforeData,
color: t.palette[1],
marker: { symbol: "circle" },
zIndex: 5,
},
],
});

// Draw connecting lines between Before and After dots via the SVG renderer
const renderer = chart.renderer;
const plotLeft = chart.plotLeft;
const plotTop = chart.plotTop;

// Build a lookup from category index → before-series point
const beforeByIdx = {};
chart.series[1].data.forEach(p => { beforeByIdx[p.y] = p; });

// Improvement per sorted row (label top 3 for focal point)
const diffs = vAfter.map((v, i) => +(v - vBefore[i]).toFixed(1));
const topN = 3;

chart.series[0].data.forEach((afterPt) => {
const beforePt = beforeByIdx[afterPt.y];
if (!beforePt) return;

renderer.path([
"M", plotLeft + beforePt.plotX, plotTop + beforePt.plotY,
"L", plotLeft + afterPt.plotX, plotTop + afterPt.plotY,
])
.attr({ stroke: t.inkSoft, "stroke-width": 2.5, "stroke-opacity": 0.35 })
.add();

// Annotate top-improvement rows with the gain value
if (afterPt.y >= n - topN) {
const midX = plotLeft + (beforePt.plotX + afterPt.plotX) / 2;
const midY = plotTop + afterPt.plotY - 14;
renderer.text(`+${diffs[afterPt.y].toFixed(1)}`, midX, midY)
.attr({ align: "center", zIndex: 6, fill: t.inkSoft })
.css({ fontSize: "11px", fontWeight: "700" })
.add();
}
});
244 changes: 244 additions & 0 deletions plots/dumbbell-basic/metadata/javascript/highcharts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
library: highcharts
language: javascript
specification_id: dumbbell-basic
created: '2026-06-30T23:00:04Z'
updated: '2026-06-30T23:18:52Z'
generated_by: claude-sonnet
workflow_run: 28480907844
issue: 945
language_version: 22.23.0
library_version: 12.6.0
preview_url_light: https://storage.googleapis.com/anyplot-images/plots/dumbbell-basic/javascript/highcharts/plot-light.png
preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/dumbbell-basic/javascript/highcharts/plot-dark.png
preview_html_light: https://storage.googleapis.com/anyplot-images/plots/dumbbell-basic/javascript/highcharts/plot-light.html
preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/dumbbell-basic/javascript/highcharts/plot-dark.html
quality_score: 89
review:
strengths:
- Sorting by improvement delta and annotating the top-3 rows turns a simple before/after
chart into a readable ranking that immediately answers 'who benefited most?'
- 'SVG renderer path drawing for connecting lines is idiomatic core-Highcharts:
bypasses the unavailable highcharts-more dumbbell type without any workaround
or CDN fallback.'
- Full theme-adaptive chrome via t.ink / t.inkSoft / t.grid — both renders are visually
correct with zero dark-on-dark failures.
- 'Perfect Imprint palette application: brand green (#009E73) for the positive ''After''
outcome is semantically apt.'
weaknesses:
- All satisfaction deltas are positive — adding one department that barely improved
or slightly regressed would better showcase the dumbbell chart's ability to show
directional variation, not just magnitude.
- Annotation font (11px) is slightly smaller than the 14px tick label baseline;
aligning it to 13–14px would improve consistency across the text hierarchy.
- 'The legend box frame is visible (default Highcharts border); removing it (legend:
{ borderWidth: 0 }) would polish the minimal-chrome aesthetic.'
image_description: |-
Light render (plot-light.png):
Background: Warm off-white (#FAF8F1) — correct theme surface.
Chrome: Title "dumbbell-basic · javascript · highcharts · anyplot.ai" at 22px bold dark text — fully readable. Subtitle "Employee satisfaction before/after workplace wellness initiative" at 14px inkSoft — readable. X-axis title "Satisfaction Score (0–10)" at 16px — readable. Y-axis category labels (Marketing, Finance, Product, Sales, Engineering, Operations, Legal, HR) at 14px — all readable. X-axis tick labels 5–9 at 14px — readable. Legend labels "After" and "Before" at 14px — readable. Annotation labels +1.6, +1.6, +1.5 at 11px — readable but slightly small.
Data: Brand green (#009E73) dots for "After" series; Imprint lavender (#C475FD) for "Before" series. SVG-rendered connecting lines at opacity 0.35. Horizontal dumbbell layout with 8 departments sorted ascending by improvement.
Legibility verdict: PASS

Dark render (plot-dark.png):
Background: Warm near-black (#1A1A17) — correct theme surface.
Chrome: All text rendered in light color via t.ink / t.inkSoft tokens. Title, subtitle, axis labels, tick labels, category labels, and legend text are all light-colored and clearly readable against the dark background. No dark-on-dark failures detected.
Data: Data colors identical to light render — green (#009E73) for After, lavender (#C475FD) for Before. Both colors remain clearly visible and distinguishable on the dark surface.
Legibility verdict: PASS
criteria_checklist:
visual_quality:
score: 29
max: 30
items:
- id: VQ-01
name: Text Legibility
score: 7
max: 8
passed: true
comment: 'All font sizes explicitly set; both themes fully readable. Minor
deduction: annotation labels at 11px slightly below 14px baseline.'
- id: VQ-02
name: No Overlap
score: 6
max: 6
passed: true
comment: No collisions between text, dots, or annotations in either render.
- id: VQ-03
name: Element Visibility
score: 6
max: 6
passed: true
comment: Marker radius 9px CSS appropriate for 8 rows; connecting lines clearly
visible at stroke-width 2.5.
- id: VQ-04
name: Color Accessibility
score: 2
max: 2
passed: true
comment: Green vs lavender provides strong hue and luminance contrast; CVD-safe.
- id: VQ-05
name: Layout & Canvas
score: 4
max: 4
passed: true
comment: Explicit margins produce well-balanced whitespace. Canvas gate passed.
- id: VQ-06
name: Axis Labels & Title
score: 2
max: 2
passed: true
comment: X-axis 'Satisfaction Score (0-10)' descriptive with units; Y-axis
null (self-labeling categories).
- id: VQ-07
name: Palette Compliance
score: 2
max: 2
passed: true
comment: After = t.palette[0] (#009E73), Before = t.palette[1] (#C475FD).
Background transparent over pageBg. Both themes correct.
design_excellence:
score: 13
max: 20
items:
- id: DE-01
name: Aesthetic Sophistication
score: 5
max: 8
passed: true
comment: 'Above default: intentional semantic palette use, sorted data, selective
annotations. Not publication-ready.'
- id: DE-02
name: Visual Refinement
score: 4
max: 6
passed: true
comment: Connecting lines at 0.35 opacity; horizontal grid suppressed; no
marker outlines. Solid refinement, legend border remains.
- id: DE-03
name: Data Storytelling
score: 4
max: 6
passed: true
comment: Sort by improvement creates ranking narrative; top-3 annotations
add focal layer; subtitle provides context.
spec_compliance:
score: 15
max: 15
items:
- id: SC-01
name: Plot Type
score: 5
max: 5
passed: true
comment: Correct horizontal dumbbell / connected dot plot.
- id: SC-02
name: Required Features
score: 4
max: 4
passed: true
comment: Two distinct-color dots per category, connecting lines, horizontal
orientation, sorted by difference.
- id: SC-03
name: Data Mapping
score: 3
max: 3
passed: true
comment: Categories on Y-axis, satisfaction score on X-axis, all 8 rows visible.
- id: SC-04
name: Title & Legend
score: 3
max: 3
passed: true
comment: Title exactly 'dumbbell-basic · javascript · highcharts · anyplot.ai'.
Legend labels 'After' and 'Before' match series.
data_quality:
score: 14
max: 15
items:
- id: DQ-01
name: Feature Coverage
score: 5
max: 6
passed: true
comment: '8 departments with varying improvement magnitudes (1.3-1.6). Minor
deduction: all diffs positive, no mixed-direction variation.'
- id: DQ-02
name: Realistic Context
score: 5
max: 5
passed: true
comment: Employee satisfaction before/after wellness initiative with named
real-world departments. Neutral, credible.
- id: DQ-03
name: Appropriate Scale
score: 4
max: 4
passed: true
comment: Baseline 5.4-7.1, post-initiative 7.0-8.4 on 0-10 scale. Improvements
of 1.3-1.6 realistic for successful program.
code_quality:
score: 10
max: 10
items:
- id: CQ-01
name: KISS Structure
score: 3
max: 3
passed: true
comment: 'Linear: data → sort → Highcharts.chart() → SVG renderer loop. No
functions or classes.'
- id: CQ-02
name: Reproducibility
score: 2
max: 2
passed: true
comment: All data hardcoded; deterministic sort; no RNG.
- id: CQ-03
name: Clean Imports
score: 2
max: 2
passed: true
comment: No imports; uses only Highcharts global and window.ANYPLOT_TOKENS.
- id: CQ-04
name: Code Elegance
score: 2
max: 2
passed: true
comment: Clean, well-commented, appropriately complex. SVG renderer used correctly.
- id: CQ-05
name: Output & API
score: 1
max: 1
passed: true
comment: Harness emits plot-light.png/dark.png + HTML. Current Highcharts
12.6.0 API.
library_mastery:
score: 8
max: 10
items:
- id: LM-01
name: Idiomatic Usage
score: 5
max: 5
passed: true
comment: Scatter series (correct core-bundle approach when dumbbell requires
highcharts-more), animation:false both locations, no explicit size, credits
disabled.
- id: LM-02
name: Distinctive Features
score: 3
max: 5
passed: true
comment: Uses chart.renderer.path() for connecting lines and renderer.text()
for annotations — distinctly Highcharts SVG-renderer features.
verdict: APPROVED
impl_tags:
dependencies: []
techniques:
- annotations
- html-export
patterns:
- data-generation
- iteration-over-groups
dataprep: []
styling:
- alpha-blending
Loading