diff --git a/plots/venn-labeled-items/implementations/javascript/d3.js b/plots/venn-labeled-items/implementations/javascript/d3.js new file mode 100644 index 0000000000..bc271f3893 --- /dev/null +++ b/plots/venn-labeled-items/implementations/javascript/d3.js @@ -0,0 +1,135 @@ +// anyplot.ai +// venn-labeled-items: Chartgeist-Style Venn Diagram with Labeled Items +// Library: d3 7.9.0 | JavaScript 22.23.0 +// Quality: 87/100 | Created: 2026-06-25 +//# anyplot-orientation: square + +const t = window.ANYPLOT_TOKENS; +const { width, height } = window.ANYPLOT_SIZE; +const THEME = window.ANYPLOT_THEME || "light"; +const inkMuted = THEME === "dark" ? "#A8A79F" : "#6B6A63"; + +// --- Data --- +const circleData = [ + { name: "Overhyped", cx: 600, cy: 480, r: 270 }, + { name: "Actually Useful", cx: 738, cy: 718, r: 270 }, + { name: "Secretly Loved", cx: 462, cy: 718, r: 270 }, +]; + +// D3 ordinal scale maps category names to Imprint palette in canonical order +const colorScale = d3.scaleOrdinal() + .domain(circleData.map(d => d.name)) + .range(t.palette.slice(0, 3)); + +// Centroid of each Venn zone for label placement +const ZONE_CENTROIDS = { + A: { x: 600, y: 314 }, + B: { x: 878, y: 800 }, + C: { x: 322, y: 800 }, + AB: { x: 730, y: 566 }, + AC: { x: 470, y: 566 }, + BC: { x: 600, y: 840 }, + ABC: { x: 600, y: 638 }, + outside: { x: 975, y: 360 }, +}; + +const items = [ + { label: "NFTs", zone: "A" }, + { label: "Metaverse", zone: "A" }, + { label: "Segway", zone: "A" }, + { label: "Google Glass", zone: "A" }, + { label: "Wikipedia", zone: "B" }, + { label: "GPS Navigation", zone: "B" }, + { label: "Cloud Backup", zone: "B" }, + { label: "Password Managers", zone: "B" }, + { label: "Spreadsheets", zone: "C" }, + { label: "Fax Machines", zone: "C" }, + { label: "Cable TV", zone: "C" }, + { label: "ChatGPT", zone: "AB" }, + { label: "Electric Scooters", zone: "AB" }, + { label: "TikTok", zone: "AC" }, + { label: "Gamification", zone: "AC" }, + { label: "Dark Mode", zone: "BC" }, + { label: "RSS Feeds", zone: "BC" }, + { label: "Sourdough", zone: "ABC" }, + { label: "Zoom", zone: "ABC" }, + { label: "Landlines", zone: "outside" }, +]; + +// Use d3.group to partition items by zone, then compute vertical stacking positions +const SPACING = 24; +const itemsByZone = d3.group(items, d => d.zone); + +itemsByZone.forEach((zoneItems, zone) => { + const { x: zx, y: zy } = ZONE_CENTROIDS[zone]; + const totalH = (zoneItems.length - 1) * SPACING; + zoneItems.forEach((item, i) => { + item.x = zx; + item.y = zy - totalH / 2 + i * SPACING; + }); +}); + +// --- SVG --- +const svg = d3.select("#container").append("svg") + .attr("width", width) + .attr("height", height); + +// --- Circles (data join, semi-transparent fills, colored strokes) --- +svg.selectAll("circle.venn-circle").data(circleData).join("circle") + .attr("class", "venn-circle") + .attr("cx", d => d.cx).attr("cy", d => d.cy).attr("r", d => d.r) + .attr("fill", d => colorScale(d.name)).attr("fill-opacity", 0.12) + .attr("stroke", d => colorScale(d.name)) + .attr("stroke-width", 2.5).attr("stroke-opacity", 0.65); + +// --- Category labels (data join per circle, editorial serif) --- +const catFont = "Georgia, 'Times New Roman', serif"; + +const catLabelDefs = [ + { name: "Overhyped", lines: ["Overhyped"], x: circleData[0].cx, y: circleData[0].cy - circleData[0].r - 20, anchor: "middle" }, + { name: "Actually Useful", lines: ["Actually", "Useful"], x: circleData[1].cx + circleData[1].r + 26, y: circleData[1].cy - 13, anchor: "start" }, + { name: "Secretly Loved", lines: ["Secretly", "Loved"], x: circleData[2].cx - circleData[2].r - 26, y: circleData[2].cy - 13, anchor: "end" }, +]; + +catLabelDefs.forEach(def => { + const catG = svg.append("g"); + catG.selectAll("text").data(def.lines).join("text") + .attr("x", def.x) + .attr("y", (_, i) => def.y + i * 28) + .attr("text-anchor", def.anchor) + .attr("fill", colorScale(def.name)) + .style("font-size", "20px").style("font-weight", "700") + .style("font-family", catFont) + .text(d => d); +}); + +// --- "outside all circles" annotation --- +const outsideItems = items.filter(d => d.zone === "outside"); +if (outsideItems.length) { + svg.append("text") + .attr("x", ZONE_CENTROIDS.outside.x) + .attr("y", d3.min(outsideItems, d => d.y) - 22) + .attr("text-anchor", "middle").attr("fill", inkMuted) + .style("font-size", "14px").style("font-style", "italic") + .text("outside all circles"); +} + +// --- Items (data join over all items, positions from d3.group stacking) --- +svg.selectAll("text.venn-item").data(items).join("text") + .attr("class", "venn-item") + .attr("x", d => d.x) + .attr("y", d => d.y) + .attr("text-anchor", "middle") + .attr("dominant-baseline", "middle") + .attr("fill", d => d.zone === "outside" ? inkMuted : t.inkSoft) + .style("font-size", "14px") + .style("font-style", d => d.zone === "outside" ? "italic" : "normal") + .style("font-family", "system-ui, -apple-system, sans-serif") + .text(d => d.label); + +// --- Title --- +svg.append("text") + .attr("x", width / 2).attr("y", 46) + .attr("text-anchor", "middle").attr("fill", t.ink) + .style("font-size", "22px").style("font-weight", "600") + .text("venn-labeled-items · javascript · d3 · anyplot.ai"); diff --git a/plots/venn-labeled-items/metadata/javascript/d3.yaml b/plots/venn-labeled-items/metadata/javascript/d3.yaml new file mode 100644 index 0000000000..87ea44ee55 --- /dev/null +++ b/plots/venn-labeled-items/metadata/javascript/d3.yaml @@ -0,0 +1,252 @@ +library: d3 +language: javascript +specification_id: venn-labeled-items +created: '2026-06-25T11:44:36Z' +updated: '2026-06-25T12:15:23Z' +generated_by: claude-sonnet +workflow_run: 28167148901 +issue: 5364 +language_version: 22.23.0 +library_version: 7.9.0 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/venn-labeled-items/javascript/d3/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/venn-labeled-items/javascript/d3/plot-dark.png +preview_html_light: https://storage.googleapis.com/anyplot-images/plots/venn-labeled-items/javascript/d3/plot-light.html +preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/venn-labeled-items/javascript/d3/plot-dark.html +quality_score: 87 +review: + strengths: + - 'Perfect spec compliance: all seven zones plus outside populated, semi-transparent + fills, category labels outside circles in their correct colors' + - 'Excellent data quality: real witty editorial items with believable zone assignments + that fit the WIRED Chartgeist aesthetic' + - Clean idiomatic D3 using d3.group data joins and ANYPLOT_TOKENS/ANYPLOT_SIZE correctly + - 'Full theme adaptation: both light and dark renders pass readability with no dark-on-dark + failures' + - Serif category labels (Georgia) reinforce the editorial magazine aesthetic called + for in the spec + weaknesses: + - Item labels at 14px are at the lower end for a 2400 px canvas — bumping to 15-16px + would improve mobile readability without crowding zones + - 'No visual hierarchy among items: all render identically (same color, size) — + consider slightly larger or bolder text for ABC intersection items (Sourdough, + Zoom) to make the triple-overlap punchline pop' + - Optional editorial subtitle absent — adding a light subtitle like 'A Cultural + Status Report' in smaller italic serif would elevate the editorial feel + - Zone centroids for outside items (x=975) sit close to the right canvas edge; a + small left-shift would provide more breathing room + image_description: |- + Light render (plot-light.png): + Background: Warm off-white (#FAF8F1) — correct theme surface + Chrome: Title "venn-labeled-items · javascript · d3 · anyplot.ai" at top in dark ink (22px, weight 600) — fully readable. Category labels in bold Georgia serif in their respective palette colors (green/lavender/blue) — clearly visible. No axis labels or tick labels (not applicable for Venn). + Data: Three overlapping circles — green (#009E73) for Overhyped, lavender (#C475FD) for Actually Useful, blue (#4467A3) for Secretly Loved. 12% opacity fills show overlapping regions. Item labels in 14px sans-serif using inkSoft token. Outside items in muted italic. + Legibility verdict: PASS — all text readable against light background; no light-on-light issues + + Dark render (plot-dark.png): + Background: Warm near-black (#1A1A17) — correct theme surface + Chrome: Title and item labels switch to light-colored text (inkSoft token — light gray) — clearly readable against the dark surface. Category labels remain in the same palette colors (green/lavender/blue), all readable against dark background. No dark-on-dark failures detected. + Data: Data colors are identical to the light render — same green, lavender, and blue circle strokes and fills. The 12% opacity fills create subtle pools against the near-black background, maintaining overlap visibility. + Legibility verdict: PASS — all text readable; chrome correctly adapts to dark theme + criteria_checklist: + visual_quality: + score: 28 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: 'All font sizes explicitly set (title 22px, category labels 20px + bold, items 14px). Readable in both themes. Minor: item labels at 14px are + at the lower bound for 2400px canvas.' + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: Vertical stacking with 24px spacing prevents any text collision across + all seven zones plus outside. + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: Circle strokes clearly delineate rings; 12% fills appropriately transparent; + text-only items suit the editorial spec. + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Imprint palette is CVD-safe; semi-transparent fills let background + show through. + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: true + comment: 'Well-centered diagram filling ~70% of canvas. Minor: upper-right + outside zone leaves some empty space.' + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: No axes (correct for Venn); title in required format; category names + as clear zone labels. + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First circle #009E73; positions 2-3 canonical Imprint order; background + #FAF8F1 light / #1A1A17 dark; chrome adapts correctly.' + design_excellence: + score: 12 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: 'Intentional editorial choices: bold Georgia serif for category labels, + italic for outside items, color-matched labels. Above defaults but not publication-ready.' + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Gridless, no spines, generous whitespace, semi-transparent fills + with matching strokes. Good refinement. + - id: DE-03 + name: Data Storytelling + score: 3 + max: 6 + passed: false + comment: Good editorial item choices but no visual hierarchy — all items render + identically, missing opportunity to emphasize the ABC triple-overlap punchline. + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct three-circle Venn with symmetric layout, equally-sized circles, + clear overlaps. + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Semi-transparent fills, category names outside circles, items as + labeled text, all 7 zones + outside populated, gridless. + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: All items placed in correct zones; outside items separated from circles. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title exactly 'venn-labeled-items · javascript · d3 · anyplot.ai'. + No series legend needed; category labels fulfill the role. + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: All 7 zones plus outside populated; good distribution across zone + types. + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Real pop-culture items with believable editorial judgment; matches + WIRED Chartgeist aesthetic. + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: Zone assignments are plausible and witty; NFTs/Metaverse as Overhyped, + GPS/Wikipedia as Actually Useful, Sourdough in all three. + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Data → SVG → circles → category labels → outside annotation → items + → title. No functions or classes. + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: All data hardcoded; fully deterministic. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: d3 is the single global; no unused imports. + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Uses d3.group for zone stacking, data joins throughout; clean and + idiomatic. + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: D3 interactive library; harness emits plot-{theme}.png + plot-{theme}.html + correctly. + library_mastery: + score: 7 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Good use of d3.group (D3 v6+ API), d3.scaleOrdinal, .data().join() + data-join, d3.min, correct ANYPLOT_TOKENS/ANYPLOT_SIZE usage. + - id: LM-02 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: d3.group for grouping/stacking is D3-idiomatic, but Venn geometry + is entirely manually positioned rather than algorithmically derived using + D3 math utilities. + verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - annotations + patterns: + - data-generation + - iteration-over-groups + dataprep: [] + styling: + - alpha-blending + - minimal-chrome