From 18ca82b561a3f5609a97b3b18b9eb5b016c95b93 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:48:43 +0000 Subject: [PATCH 1/5] feat(d3): implement slope-basic --- .../implementations/javascript/d3.js | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 plots/slope-basic/implementations/javascript/d3.js diff --git a/plots/slope-basic/implementations/javascript/d3.js b/plots/slope-basic/implementations/javascript/d3.js new file mode 100644 index 0000000000..fc968b6915 --- /dev/null +++ b/plots/slope-basic/implementations/javascript/d3.js @@ -0,0 +1,172 @@ +// anyplot.ai +// slope-basic: Basic Slope Chart (Slopegraph) +// Library: d3 7.9.0 | JavaScript 22 +// Quality: pending | Created: 2026-07-25 + +const t = window.ANYPLOT_TOKENS; +const { width, height } = window.ANYPLOT_SIZE; + +const margin = { top: 150, right: 260, bottom: 50, left: 260 }; +const iw = width - margin.left - margin.right; +const ih = height - margin.top - margin.bottom; + +// --- Data: employee satisfaction score (0-100) by department, 2019 vs 2024 survey +const data = [ + { department: "Engineering", year2019: 72, year2024: 81 }, + { department: "Sales", year2019: 68, year2024: 61 }, + { department: "Marketing", year2019: 75, year2024: 79 }, + { department: "HR", year2019: 80, year2024: 74 }, + { department: "Finance", year2019: 65, year2024: 70 }, + { department: "Customer Support", year2019: 58, year2024: 52 }, + { department: "Operations", year2019: 70, year2024: 77 }, + { department: "Product", year2019: 77, year2024: 83 }, + { department: "Legal", year2019: 82, year2024: 80 }, + { department: "IT", year2019: 63, year2024: 69 }, +]; + +// --- Scale -------------------------------------------------------------------- +const allValues = data.flatMap((d) => [d.year2019, d.year2024]); +const y = d3 + .scaleLinear() + .domain([d3.min(allValues) - 5, d3.max(allValues) + 5]) + .nice() + .range([ih, 0]); + +const increaseColor = t.palette[0]; // brand green — always first series, doubles as "improved" +const decreaseColor = t.palette[4]; // matte red — Imprint semantic anchor for decline +const colorOf = (d) => (d.year2024 >= d.year2019 ? increaseColor : decreaseColor); + +// --- Label decluttering: keep the true data point, nudge stacked labels apart -- +// Slope charts pack many labels along two narrow columns; a plain scaleLinear +// position collides whenever two entities share a close value. Sort by position, +// enforce a minimum gap top-to-bottom, then re-settle from the bottom if the +// pass pushed the last label past the plot bounds. +function declutter(points, minGap) { + const sorted = points.slice().sort((a, b) => a.y - b.y); + for (let i = 1; i < sorted.length; i++) { + if (sorted[i].y - sorted[i - 1].y < minGap) sorted[i].y = sorted[i - 1].y + minGap; + } + const overflow = sorted[sorted.length - 1].y - ih; + if (overflow > 0) { + for (let i = sorted.length - 1; i >= 0; i--) { + sorted[i].y -= overflow; + if (i > 0 && sorted[i].y - sorted[i - 1].y >= minGap) break; + } + } + return sorted; +} + +const minGap = 30; +const leftLabels = declutter( + data.map((d) => ({ d, trueY: y(d.year2019), y: y(d.year2019) })), + minGap +); +const rightLabels = declutter( + data.map((d) => ({ d, trueY: y(d.year2024), y: y(d.year2024) })), + minGap +); + +// --- SVG mount ------------------------------------------------------------------ +const svg = d3.select("#container").append("svg").attr("width", width).attr("height", height); +const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`); + +// --- Vertical axes: the two survey years ---------------------------------------- +g.append("line") + .attr("x1", 0).attr("x2", 0).attr("y1", 0).attr("y2", ih) + .attr("stroke", t.inkSoft).attr("stroke-width", 1.5); +g.append("line") + .attr("x1", iw).attr("x2", iw).attr("y1", 0).attr("y2", ih) + .attr("stroke", t.inkSoft).attr("stroke-width", 1.5); + +g.append("text") + .attr("x", 0).attr("y", -30).attr("text-anchor", "middle") + .attr("fill", t.ink).style("font-size", "20px").style("font-weight", "600") + .text("2019 Survey"); +g.append("text") + .attr("x", iw).attr("y", -30).attr("text-anchor", "middle") + .attr("fill", t.ink).style("font-size", "20px").style("font-weight", "600") + .text("2024 Survey"); + +// --- Slope lines + endpoint markers ---------------------------------------------- +g.selectAll(".slope-line") + .data(data) + .join("line") + .attr("class", "slope-line") + .attr("x1", 0).attr("y1", (d) => y(d.year2019)) + .attr("x2", iw).attr("y2", (d) => y(d.year2024)) + .attr("stroke", colorOf) + .attr("stroke-width", 3) + .attr("opacity", 0.85); + +g.selectAll(".start-point") + .data(data) + .join("circle") + .attr("class", "start-point") + .attr("cx", 0).attr("cy", (d) => y(d.year2019)) + .attr("r", 6).attr("fill", colorOf); + +g.selectAll(".end-point") + .data(data) + .join("circle") + .attr("class", "end-point") + .attr("cx", iw).attr("cy", (d) => y(d.year2024)) + .attr("r", 6).attr("fill", colorOf); + +// --- Leader ticks where a label was nudged off its true position ----------------- +g.selectAll(".leader-left") + .data(leftLabels.filter((p) => Math.abs(p.y - p.trueY) > 1)) + .join("line") + .attr("class", "leader-left") + .attr("x1", 0).attr("y1", (p) => p.trueY) + .attr("x2", -16).attr("y2", (p) => p.y) + .attr("stroke", t.grid).attr("stroke-width", 1); + +g.selectAll(".leader-right") + .data(rightLabels.filter((p) => Math.abs(p.y - p.trueY) > 1)) + .join("line") + .attr("class", "leader-right") + .attr("x1", iw).attr("y1", (p) => p.trueY) + .attr("x2", iw + 16).attr("y2", (p) => p.y) + .attr("stroke", t.grid).attr("stroke-width", 1); + +// --- Endpoint labels --------------------------------------------------------------- +g.selectAll(".label-left") + .data(leftLabels) + .join("text") + .attr("class", "label-left") + .attr("x", -20).attr("y", (p) => p.y) + .attr("dy", "0.35em").attr("text-anchor", "end") + .attr("fill", t.inkSoft).style("font-size", "15px") + .text((p) => `${p.d.department} · ${p.d.year2019}`); + +g.selectAll(".label-right") + .data(rightLabels) + .join("text") + .attr("class", "label-right") + .attr("x", iw + 20).attr("y", (p) => p.y) + .attr("dy", "0.35em").attr("text-anchor", "start") + .attr("fill", t.inkSoft).style("font-size", "15px") + .text((p) => `${p.d.year2024} · ${p.d.department}`); + +// --- Legend: line direction ----------------------------------------------------- +const legend = svg.append("g").attr("transform", `translate(${width / 2 - 110}, 95)`); +legend.append("line") + .attr("x1", 0).attr("x2", 26).attr("y1", 0).attr("y2", 0) + .attr("stroke", increaseColor).attr("stroke-width", 3); +legend.append("text") + .attr("x", 34).attr("y", 5) + .attr("fill", t.inkSoft).style("font-size", "14px") + .text("Improved"); +legend.append("line") + .attr("x1", 140).attr("x2", 166).attr("y1", 0).attr("y2", 0) + .attr("stroke", decreaseColor).attr("stroke-width", 3); +legend.append("text") + .attr("x", 174).attr("y", 5) + .attr("fill", t.inkSoft).style("font-size", "14px") + .text("Declined"); + +// --- Title --------------------------------------------------------------------- +svg.append("text") + .attr("x", width / 2).attr("y", 60).attr("text-anchor", "middle") + .attr("fill", t.ink).style("font-size", "22px").style("font-weight", "600") + .text("slope-basic · javascript · d3 · anyplot.ai"); From 662aeb40d10e49faa2312a7f556a9faeec83d6b2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:48:50 +0000 Subject: [PATCH 2/5] chore(d3): add metadata for slope-basic --- plots/slope-basic/metadata/javascript/d3.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 plots/slope-basic/metadata/javascript/d3.yaml diff --git a/plots/slope-basic/metadata/javascript/d3.yaml b/plots/slope-basic/metadata/javascript/d3.yaml new file mode 100644 index 0000000000..22b2e69b41 --- /dev/null +++ b/plots/slope-basic/metadata/javascript/d3.yaml @@ -0,0 +1,21 @@ +# Per-library metadata for d3 implementation of slope-basic +# Auto-generated by impl-generate.yml + +library: d3 +language: javascript +specification_id: slope-basic +created: '2026-07-25T23:48:50Z' +updated: '2026-07-25T23:48:50Z' +generated_by: claude-sonnet +workflow_run: 30179881795 +issue: 981 +language_version: 22.23.1 +library_version: 7.9.0 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/d3/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/d3/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/d3/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/d3/plot-dark.html +quality_score: null +review: + strengths: [] + weaknesses: [] From 53aaec7360270303f892a66bf2eefc2bdab3bfdc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:53:55 +0000 Subject: [PATCH 3/5] chore(d3): update quality score 89 and review feedback for slope-basic --- .../implementations/javascript/d3.js | 4 +- plots/slope-basic/metadata/javascript/d3.yaml | 242 +++++++++++++++++- 2 files changed, 237 insertions(+), 9 deletions(-) diff --git a/plots/slope-basic/implementations/javascript/d3.js b/plots/slope-basic/implementations/javascript/d3.js index fc968b6915..ee32006c0a 100644 --- a/plots/slope-basic/implementations/javascript/d3.js +++ b/plots/slope-basic/implementations/javascript/d3.js @@ -1,7 +1,7 @@ // anyplot.ai // slope-basic: Basic Slope Chart (Slopegraph) -// Library: d3 7.9.0 | JavaScript 22 -// Quality: pending | Created: 2026-07-25 +// Library: d3 7.9.0 | JavaScript 22.23.1 +// Quality: 89/100 | Created: 2026-07-25 const t = window.ANYPLOT_TOKENS; const { width, height } = window.ANYPLOT_SIZE; diff --git a/plots/slope-basic/metadata/javascript/d3.yaml b/plots/slope-basic/metadata/javascript/d3.yaml index 22b2e69b41..c3d38ae636 100644 --- a/plots/slope-basic/metadata/javascript/d3.yaml +++ b/plots/slope-basic/metadata/javascript/d3.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for d3 implementation of slope-basic -# Auto-generated by impl-generate.yml - library: d3 language: javascript specification_id: slope-basic created: '2026-07-25T23:48:50Z' -updated: '2026-07-25T23:48:50Z' +updated: '2026-07-25T23:53:55Z' generated_by: claude-sonnet workflow_run: 30179881795 issue: 981 @@ -15,7 +12,238 @@ 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/d3/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/d3/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/d3/plot-dark.html -quality_score: null +quality_score: 89 review: - strengths: [] - weaknesses: [] + strengths: + - Custom label-decluttering algorithm (declutter()) cleanly resolves overlaps across + 10 densely-packed entities on both label columns, with subtle leader ticks connecting + nudged labels back to their true value position + - 'Correct semantic color exception applied per the Imprint style guide (green = + improved, matte red #AE3030 = declined), redundantly reinforced by the slope direction + itself so the encoding is not red-green-only' + - Theme-adaptive chrome verified correct in both renders — warm off-white / near-black + backgrounds, no dark-on-dark or light-on-light text failures, data colors identical + between themes + - Canvas is exactly 3200×1800, title format and legend labels match spec exactly, + data is fully deterministic and in-memory (no fetch/csv) + - Idiomatic D3 join patterns (selectAll().data().join()) for lines, circles, labels + and leader ticks; no fake interactivity, no animation + weaknesses: + - No unit/context label indicating what the values represent (e.g. 'Employee Satisfaction + Score (0-100)') — the bare numbers next to each label are ambiguous without an + axis title or subtitle + - The declutter() helper function deviates from the KISS 'no functions' default + — justified by the genuine label-collision problem, but consider inlining or keeping + it minimal + - Chart stays generic in its storytelling — no annotation or visual emphasis calls + out the largest mover (e.g. Customer Support's decline or Product's gain), leaving + DE-03 without a clear focal point + - Endpoint/leader label font size (15px) is proportionally small relative to the + 3200px canvas and the 22px title — bumping to ~16-17px would improve full-size + and mobile-scaled legibility + image_description: |- + Light render (plot-light.png): + Background: Warm off-white, consistent with #FAF8F1 — not pure white, not dark. + Chrome: Title "slope-basic · javascript · d3 · anyplot.ai" in bold dark ink at top-center, clearly legible. "Improved"/"Declined" legend centered below title with matching colored line swatches. "2019 Survey" and "2024 Survey" bold headers above each vertical axis line. Left labels read "Department · Value", right labels read "Value · Department", both in soft dark-gray ink, all legible against the light background with no overlap between adjacent labels (the decluttering algorithm keeps a minimum vertical gap even where raw values are close, e.g. Legal/HR at 82/80 or Finance/IT at 70/69). + Data: Ten department slope lines running left (2019) to right (2024), each with a filled circle endpoint marker. Lines colored brand green (#009E73) for departments whose 2024 score rose (e.g. Product 77→83, Engineering 72→81) and matte red for departments that declined (e.g. Legal 82→80, Sales 68→61, Customer Support 58→52). Direction is reinforced twice — by color and by the visible slope of the line itself. + Legibility verdict: PASS — all title, legend, header, and endpoint-label text is clearly readable against the light surface; no light-on-light text found. + + Dark render (plot-dark.png): + Background: Warm near-black, consistent with #1A1A17 — not pure black, not light. + Chrome: Same title, legend, and axis-header text now rendered in light ink/off-white, clearly visible against the dark surface. Vertical axis lines and leader ticks use a soft light-gray stroke that remains visible without dominating the composition. Endpoint labels in light soft-ink are readable at the same positions as the light render. + Data: Same ten slope lines in the identical brand green (#009E73) and matte red (#AE3030) hues as the light render — confirmed data colors are unchanged between themes; only the chrome (background, text, axis-line, leader-tick colors) flipped. + Legibility verdict: PASS — no dark-on-dark failures observed; every title, legend, header, and endpoint label is clearly legible against the near-black background. + criteria_checklist: + visual_quality: + score: 28 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: All text explicitly sized and readable in both themes; endpoint labels + (15px) are proportionally small relative to the 3200px canvas and title + (22px) + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: Custom decluttering algorithm prevents any label collisions across + both columns + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: Sparse data (10 entities) with prominent 6px-radius markers and 3px + stroke lines + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Green/red direction coding is redundantly reinforced by slope direction, + not a sole signal; CVD-safe Imprint palette + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Canvas exactly 3200x1800, well-proportioned margins, nothing cut + off, title comfortably sized + - id: VQ-06 + name: Axis Labels & Title + score: 1 + max: 2 + passed: false + comment: Axis headers name the time points but no unit/context label clarifies + what the plotted values represent + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First series #009E73, semantic-exception matte red for decline per + style guide, correct light/dark backgrounds' + design_excellence: + score: 14 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: Professional, restrained styling with custom typography hierarchy; + not exceptionally distinctive + - id: DE-02 + name: Visual Refinement + score: 5 + max: 6 + passed: true + comment: No unnecessary chrome, subtle leader ticks and grid, generous whitespace + - id: DE-03 + name: Data Storytelling + score: 4 + max: 6 + passed: true + comment: Color/slope hierarchy guides the eye, but no emphasis or annotation + highlights the single most notable change + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct slopegraph structure + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Endpoint labels, direction color-coding, labeled vertical axes, 10 + entities (within recommended 5-15) + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: value_start/value_end correctly mapped to left/right axes + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title format exact match; legend labels Improved/Declined match direction + semantics + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Covers all spec-listed features + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Employee satisfaction survey by department, plausible and neutral + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: Scores 52-83 on an implied 0-100 scale, sensible spread + code_quality: + score: 9 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 2 + max: 3 + passed: true + comment: Uses a declutter() helper function, deviating from the strict no-functions + default, though functionally justified + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Fully deterministic hardcoded data + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: Only the d3 global used, no import statements + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean, no fake functionality or fake UI + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: 'Mounts to #container per contract, no animation' + library_mastery: + score: 8 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Correct D3 join pattern, scaleLinear, token-driven theming + - id: LM-02 + name: Distinctive Features + score: 4 + max: 5 + passed: true + comment: Custom decluttering algorithm and leader-tick technique beyond the + basic skeleton example + verdict: REJECTED +impl_tags: + dependencies: [] + techniques: + - custom-legend + - layer-composition + patterns: + - data-generation + dataprep: [] + styling: + - alpha-blending From 54c9417fb05d10e184dfa37deddc4380d64ab30c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:59:07 +0000 Subject: [PATCH 4/5] fix(d3): address review feedback for slope-basic Attempt 1/4 - fixes based on AI review --- .../implementations/javascript/d3.js | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/plots/slope-basic/implementations/javascript/d3.js b/plots/slope-basic/implementations/javascript/d3.js index ee32006c0a..6c7681fe4b 100644 --- a/plots/slope-basic/implementations/javascript/d3.js +++ b/plots/slope-basic/implementations/javascript/d3.js @@ -6,7 +6,7 @@ const t = window.ANYPLOT_TOKENS; const { width, height } = window.ANYPLOT_SIZE; -const margin = { top: 150, right: 260, bottom: 50, left: 260 }; +const margin = { top: 175, right: 260, bottom: 50, left: 260 }; const iw = width - margin.left - margin.right; const ih = height - margin.top - margin.bottom; @@ -36,6 +36,9 @@ const increaseColor = t.palette[0]; // brand green — always first series, doub const decreaseColor = t.palette[4]; // matte red — Imprint semantic anchor for decline const colorOf = (d) => (d.year2024 >= d.year2019 ? increaseColor : decreaseColor); +// --- Storytelling focal point: call out the single largest mover --------------- +const standout = d3.greatest(data, (d) => Math.abs(d.year2024 - d.year2019)); + // --- Label decluttering: keep the true data point, nudge stacked labels apart -- // Slope charts pack many labels along two narrow columns; a plain scaleLinear // position collides whenever two entities share a close value. Sort by position, @@ -56,7 +59,7 @@ function declutter(points, minGap) { return sorted; } -const minGap = 30; +const minGap = 34; const leftLabels = declutter( data.map((d) => ({ d, trueY: y(d.year2019), y: y(d.year2019) })), minGap @@ -79,11 +82,11 @@ g.append("line") .attr("stroke", t.inkSoft).attr("stroke-width", 1.5); g.append("text") - .attr("x", 0).attr("y", -30).attr("text-anchor", "middle") + .attr("x", 0).attr("y", -20).attr("text-anchor", "middle") .attr("fill", t.ink).style("font-size", "20px").style("font-weight", "600") .text("2019 Survey"); g.append("text") - .attr("x", iw).attr("y", -30).attr("text-anchor", "middle") + .attr("x", iw).attr("y", -20).attr("text-anchor", "middle") .attr("fill", t.ink).style("font-size", "20px").style("font-weight", "600") .text("2024 Survey"); @@ -95,22 +98,31 @@ g.selectAll(".slope-line") .attr("x1", 0).attr("y1", (d) => y(d.year2019)) .attr("x2", iw).attr("y2", (d) => y(d.year2024)) .attr("stroke", colorOf) - .attr("stroke-width", 3) - .attr("opacity", 0.85); + .attr("stroke-width", (d) => (d === standout ? 5 : 3)) + .attr("opacity", (d) => (d === standout ? 1 : 0.85)); g.selectAll(".start-point") .data(data) .join("circle") .attr("class", "start-point") .attr("cx", 0).attr("cy", (d) => y(d.year2019)) - .attr("r", 6).attr("fill", colorOf); + .attr("r", (d) => (d === standout ? 8 : 6)).attr("fill", colorOf); g.selectAll(".end-point") .data(data) .join("circle") .attr("class", "end-point") .attr("cx", iw).attr("cy", (d) => y(d.year2024)) - .attr("r", 6).attr("fill", colorOf); + .attr("r", (d) => (d === standout ? 8 : 6)).attr("fill", colorOf); + +// --- Focal-point annotation: label the standout mover at its line midpoint ----- +const standoutDelta = standout.year2024 - standout.year2019; +g.append("text") + .attr("x", iw / 2) + .attr("y", (y(standout.year2019) + y(standout.year2024)) / 2 - 14) + .attr("text-anchor", "middle") + .attr("fill", colorOf(standout)).style("font-size", "16px").style("font-weight", "700") + .text(`${standout.department}: ${standoutDelta > 0 ? "+" : ""}${standoutDelta} (largest ${standoutDelta > 0 ? "gain" : "decline"})`); // --- Leader ticks where a label was nudged off its true position ----------------- g.selectAll(".leader-left") @@ -136,7 +148,7 @@ g.selectAll(".label-left") .attr("class", "label-left") .attr("x", -20).attr("y", (p) => p.y) .attr("dy", "0.35em").attr("text-anchor", "end") - .attr("fill", t.inkSoft).style("font-size", "15px") + .attr("fill", t.inkSoft).style("font-size", "17px") .text((p) => `${p.d.department} · ${p.d.year2019}`); g.selectAll(".label-right") @@ -145,11 +157,11 @@ g.selectAll(".label-right") .attr("class", "label-right") .attr("x", iw + 20).attr("y", (p) => p.y) .attr("dy", "0.35em").attr("text-anchor", "start") - .attr("fill", t.inkSoft).style("font-size", "15px") + .attr("fill", t.inkSoft).style("font-size", "17px") .text((p) => `${p.d.year2024} · ${p.d.department}`); // --- Legend: line direction ----------------------------------------------------- -const legend = svg.append("g").attr("transform", `translate(${width / 2 - 110}, 95)`); +const legend = svg.append("g").attr("transform", `translate(${width / 2 - 110}, 118)`); legend.append("line") .attr("x1", 0).attr("x2", 26).attr("y1", 0).attr("y2", 0) .attr("stroke", increaseColor).attr("stroke-width", 3); @@ -165,8 +177,12 @@ legend.append("text") .attr("fill", t.inkSoft).style("font-size", "14px") .text("Declined"); -// --- Title --------------------------------------------------------------------- +// --- Title + subtitle ------------------------------------------------------------ svg.append("text") .attr("x", width / 2).attr("y", 60).attr("text-anchor", "middle") .attr("fill", t.ink).style("font-size", "22px").style("font-weight", "600") .text("slope-basic · javascript · d3 · anyplot.ai"); +svg.append("text") + .attr("x", width / 2).attr("y", 88).attr("text-anchor", "middle") + .attr("fill", t.inkSoft).style("font-size", "16px") + .text("Employee Satisfaction Score (0-100 scale)"); From f760853a7d574b20b24b701f68301d4e4a7d4c61 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Jul 2026 00:03:19 +0000 Subject: [PATCH 5/5] chore(d3): update quality score 92 and review feedback for slope-basic --- .../implementations/javascript/d3.js | 2 +- plots/slope-basic/metadata/javascript/d3.yaml | 104 +++++++++--------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/plots/slope-basic/implementations/javascript/d3.js b/plots/slope-basic/implementations/javascript/d3.js index 6c7681fe4b..5db36b9845 100644 --- a/plots/slope-basic/implementations/javascript/d3.js +++ b/plots/slope-basic/implementations/javascript/d3.js @@ -1,7 +1,7 @@ // anyplot.ai // slope-basic: Basic Slope Chart (Slopegraph) // Library: d3 7.9.0 | JavaScript 22.23.1 -// Quality: 89/100 | Created: 2026-07-25 +// Quality: 92/100 | Updated: 2026-07-26 const t = window.ANYPLOT_TOKENS; const { width, height } = window.ANYPLOT_SIZE; diff --git a/plots/slope-basic/metadata/javascript/d3.yaml b/plots/slope-basic/metadata/javascript/d3.yaml index c3d38ae636..63f2220970 100644 --- a/plots/slope-basic/metadata/javascript/d3.yaml +++ b/plots/slope-basic/metadata/javascript/d3.yaml @@ -2,7 +2,7 @@ library: d3 language: javascript specification_id: slope-basic created: '2026-07-25T23:48:50Z' -updated: '2026-07-25T23:53:55Z' +updated: '2026-07-26T00:03:19Z' generated_by: claude-sonnet workflow_run: 30179881795 issue: 981 @@ -12,60 +12,57 @@ 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/d3/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/d3/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/javascript/d3/plot-dark.html -quality_score: 89 +quality_score: 92 review: strengths: - - Custom label-decluttering algorithm (declutter()) cleanly resolves overlaps across - 10 densely-packed entities on both label columns, with subtle leader ticks connecting - nudged labels back to their true value position - - 'Correct semantic color exception applied per the Imprint style guide (green = - improved, matte red #AE3030 = declined), redundantly reinforced by the slope direction - itself so the encoding is not red-green-only' + - 'All four prior weaknesses fixed: added the ''Employee Satisfaction Score (0-100 + scale)'' subtitle (VQ-06 units), bumped endpoint-label font from 15px to 17px + for better hierarchy against the 22px title (VQ-01), and added a standout annotation + (''Engineering: +9 (largest gain)'') with a thickened/darker highlighted line + giving the chart a clear DE-03 focal point' + - Custom label-decluttering algorithm (declutter()) still cleanly resolves overlaps + across 10 densely-packed entities on both label columns, with subtle leader ticks + connecting nudged labels back to their true value position + - 'Correct semantic color exception per the Imprint style guide (green = improved, + matte red #AE3030 = declined), redundantly reinforced by the slope direction itself + so the encoding is never red-green-only' - Theme-adaptive chrome verified correct in both renders — warm off-white / near-black backgrounds, no dark-on-dark or light-on-light text failures, data colors identical between themes - - Canvas is exactly 3200×1800, title format and legend labels match spec exactly, + - Canvas is exactly 3200x1800, title format and legend labels match spec exactly, data is fully deterministic and in-memory (no fetch/csv) - - Idiomatic D3 join patterns (selectAll().data().join()) for lines, circles, labels - and leader ticks; no fake interactivity, no animation weaknesses: - - No unit/context label indicating what the values represent (e.g. 'Employee Satisfaction - Score (0-100)') — the bare numbers next to each label are ambiguous without an - axis title or subtitle - - The declutter() helper function deviates from the KISS 'no functions' default - — justified by the genuine label-collision problem, but consider inlining or keeping - it minimal - - Chart stays generic in its storytelling — no annotation or visual emphasis calls - out the largest mover (e.g. Customer Support's decline or Product's gain), leaving - DE-03 without a clear focal point - - Endpoint/leader label font size (15px) is proportionally small relative to the - 3200px canvas and the 22px title — bumping to ~16-17px would improve full-size - and mobile-scaled legibility + - The declutter() helper function still deviates from the KISS 'no functions' default + (CQ-01) — justified by the genuine label-collision problem across 10 entities, + but keep an eye on it if further complexity is added + - DE-01 aesthetic sophistication is solid and professional but not yet at the 'FiveThirtyEight-level' + distinctive-palette tier — mostly restrained typography and semantic color rather + than a truly bespoke visual signature image_description: |- Light render (plot-light.png): Background: Warm off-white, consistent with #FAF8F1 — not pure white, not dark. - Chrome: Title "slope-basic · javascript · d3 · anyplot.ai" in bold dark ink at top-center, clearly legible. "Improved"/"Declined" legend centered below title with matching colored line swatches. "2019 Survey" and "2024 Survey" bold headers above each vertical axis line. Left labels read "Department · Value", right labels read "Value · Department", both in soft dark-gray ink, all legible against the light background with no overlap between adjacent labels (the decluttering algorithm keeps a minimum vertical gap even where raw values are close, e.g. Legal/HR at 82/80 or Finance/IT at 70/69). - Data: Ten department slope lines running left (2019) to right (2024), each with a filled circle endpoint marker. Lines colored brand green (#009E73) for departments whose 2024 score rose (e.g. Product 77→83, Engineering 72→81) and matte red for departments that declined (e.g. Legal 82→80, Sales 68→61, Customer Support 58→52). Direction is reinforced twice — by color and by the visible slope of the line itself. - Legibility verdict: PASS — all title, legend, header, and endpoint-label text is clearly readable against the light surface; no light-on-light text found. + Chrome: Title "slope-basic · javascript · d3 · anyplot.ai" in bold dark ink at top-center, clearly legible, with a new subtitle "Employee Satisfaction Score (0-100 scale)" directly beneath it giving the values units/context. "Improved"/"Declined" legend centered below the subtitle with matching colored line swatches. "2019 Survey" and "2024 Survey" bold headers above each vertical axis line. Left labels read "Department · Value", right labels read "Value · Department", both in soft dark-gray ink at 17px, all legible against the light background with no overlap between adjacent labels (the decluttering algorithm keeps a minimum vertical gap even where raw values are close, e.g. Legal/HR at 82/80 or Finance/IT at 70/69, using thin leader ticks to reconnect nudged labels to their true value position). + Data: Ten department slope lines running left (2019) to right (2024), each with a filled circle endpoint marker. Lines colored brand green (#009E73) for departments whose 2024 score rose (e.g. Product 77→83, Engineering 72→81) and matte red (#AE3030) for departments that declined (e.g. Legal 82→80, Sales 68→61, Customer Support 58→52). Direction is reinforced twice — by color and by the visible slope of the line itself. Engineering's line is now visibly thicker/darker than the rest, with a bold annotation "Engineering: +9 (largest gain)" at its midpoint calling out the single largest mover — a clear storytelling focal point that was missing in the previous attempt. + Legibility verdict: PASS — all title, subtitle, legend, header, annotation, and endpoint-label text is clearly readable against the light surface; no light-on-light text found. Dark render (plot-dark.png): Background: Warm near-black, consistent with #1A1A17 — not pure black, not light. - Chrome: Same title, legend, and axis-header text now rendered in light ink/off-white, clearly visible against the dark surface. Vertical axis lines and leader ticks use a soft light-gray stroke that remains visible without dominating the composition. Endpoint labels in light soft-ink are readable at the same positions as the light render. - Data: Same ten slope lines in the identical brand green (#009E73) and matte red (#AE3030) hues as the light render — confirmed data colors are unchanged between themes; only the chrome (background, text, axis-line, leader-tick colors) flipped. - Legibility verdict: PASS — no dark-on-dark failures observed; every title, legend, header, and endpoint label is clearly legible against the near-black background. + Chrome: Same title, subtitle, legend, and axis-header text now rendered in light ink/off-white, clearly visible against the dark surface. Vertical axis lines and leader ticks use a soft light-gray stroke that remains visible without dominating the composition. Endpoint labels in light soft-ink are readable at the same positions as the light render. + Data: Same ten slope lines in the identical brand green (#009E73) and matte red (#AE3030) hues as the light render — confirmed data colors are unchanged between themes; only the chrome (background, text, axis-line, leader-tick colors) flipped. The Engineering standout annotation and thickened line are equally prominent and legible in this theme. + Legibility verdict: PASS — no dark-on-dark failures observed; every title, subtitle, legend, header, annotation, and endpoint label is clearly legible against the near-black background. criteria_checklist: visual_quality: - score: 28 + score: 30 max: 30 items: - id: VQ-01 name: Text Legibility - score: 7 + score: 8 max: 8 passed: true - comment: All text explicitly sized and readable in both themes; endpoint labels - (15px) are proportionally small relative to the 3200px canvas and title - (22px) + comment: Endpoint labels bumped from 15px to 17px, now well-balanced against + the 22px title/20px axis headers/16px subtitle; all explicitly sized and + readable in both themes and at 400px mobile scale - id: VQ-02 name: No Overlap score: 6 @@ -78,8 +75,8 @@ review: score: 6 max: 6 passed: true - comment: Sparse data (10 entities) with prominent 6px-radius markers and 3px - stroke lines + comment: Sparse data (10 entities) with prominent 6-8px-radius markers and + 3-5px stroke lines, standout entity emphasized further - id: VQ-04 name: Color Accessibility score: 2 @@ -93,23 +90,24 @@ review: max: 4 passed: true comment: Canvas exactly 3200x1800, well-proportioned margins, nothing cut - off, title comfortably sized + off, title/subtitle/legend comfortably sized - id: VQ-06 name: Axis Labels & Title - score: 1 + score: 2 max: 2 - passed: false - comment: Axis headers name the time points but no unit/context label clarifies - what the plotted values represent + passed: true + comment: New subtitle 'Employee Satisfaction Score (0-100 scale)' now supplies + the units/context that was missing in attempt 1 - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true comment: 'First series #009E73, semantic-exception matte red for decline per - style guide, correct light/dark backgrounds' + style guide, correct light/dark backgrounds, identical data colors across + themes' design_excellence: - score: 14 + score: 15 max: 20 items: - id: DE-01 @@ -118,20 +116,21 @@ review: max: 8 passed: true comment: Professional, restrained styling with custom typography hierarchy; - not exceptionally distinctive + solid but not yet a fully distinctive bespoke design - id: DE-02 name: Visual Refinement score: 5 max: 6 passed: true - comment: No unnecessary chrome, subtle leader ticks and grid, generous whitespace + comment: No unnecessary chrome, subtle leader ticks, generous whitespace - id: DE-03 name: Data Storytelling - score: 4 + score: 5 max: 6 passed: true - comment: Color/slope hierarchy guides the eye, but no emphasis or annotation - highlights the single most notable change + comment: New standout annotation + thickened/darkened line for Engineering + (+9, largest gain) gives the chart a clear focal point on top of the existing + color/slope hierarchy — the prior 'no emphasis' weakness is resolved spec_compliance: score: 15 max: 15 @@ -194,7 +193,7 @@ review: max: 3 passed: true comment: Uses a declutter() helper function, deviating from the strict no-functions - default, though functionally justified + default, though functionally justified by genuine label-collision handling - id: CQ-02 name: Reproducibility score: 2 @@ -234,14 +233,15 @@ review: score: 4 max: 5 passed: true - comment: Custom decluttering algorithm and leader-tick technique beyond the - basic skeleton example - verdict: REJECTED + comment: Custom decluttering algorithm, leader-tick technique, and standout-annotation + emphasis beyond the basic skeleton example + verdict: APPROVED impl_tags: dependencies: [] techniques: - custom-legend - layer-composition + - annotations patterns: - data-generation dataprep: []