# Converting Static Figures to Interactive D3 Charts This guide explains how to convert PNG figures into interactive D3.js visualizations for this project. ## Overview Each interactive chart consists of: 1. **JSON data file** in `app/public/data/` (served at `/data/filename.json`) 2. **HTML embed file** in `app/src/content/embeds/` (e.g., `chart-name.html`) 3. **MDX integration** using the `HtmlEmbed` component ## File Structure ``` app/ ├── public/data/ # JSON data (served at /data/*) │ ├── overall_performance.json │ ├── calibration_curves.json │ └── ... └── src/content/embeds/ # HTML chart implementations ├── banner.html # Example: scatter plot └── calibration-curves.html # (to create) ``` ## Step 1: Understand Your Data Check the JSON structure in `app/public/data/`. Common patterns: **Scatter plot** (`overall_performance.json`): ```json { "models": [ { "name": "Model A", "avg_score": 15.8, "avg_output_tokens_per_turn": 5253, "color": "#FF6B00", "is_open": false } ] } ``` **Line chart / Calibration** (`calibration_curves.json`): ```json { "models": [ { "name": "Model A", "color": "#FF6B00", "calibration_points": [ { "confidence_level": 5, "actual_success_rate": 0.041, "sample_count": 73 } ] } ] } ``` **Histogram** (`confidence_distribution.json`): ```json { "models": [ { "name": "Model A", "color": "#FF6B00", "total_guesses": 579, "distribution": [ { "confidence_level": 5, "proportion": 0.024, "count": 14 } ] } ] } ``` ## Step 2: Create the HTML Embed Create a new file in `app/src/content/embeds/`. Use this template: ```html
``` ## Step 3: Key Implementation Details ### CSS Variables (Theme Support) Always use CSS variables for colors that need to adapt to light/dark mode: | Variable | Purpose | |----------|---------| | `var(--text-color)` | Main text, labels | | `var(--muted-color)` | Secondary text, tick labels | | `var(--border-color)` | Borders, outlines | | `var(--surface-bg)` | Tooltip background | | `var(--page-bg)` | Page background | ### D3 Patterns Used **Scale setup:** ```javascript const xExtent = d3.extent(data, d => d.x); const xPadding = (xExtent[1] - xExtent[0]) * 0.1; xScale.domain([xExtent[0] - xPadding, xExtent[1] + xPadding]) .range([0, innerWidth]) .nice(); ``` **Grid lines:** ```javascript gGrid.selectAll('.grid-x') .data(xScale.ticks(6)) .join('line') .attr('class', 'grid-x') .attr('x1', d => xScale(d)) .attr('x2', d => xScale(d)) .attr('y1', 0) .attr('y2', innerHeight); ``` **Axes (basic):** ```javascript gAxes.selectAll('.x-axis') .data([0]) .join('g') .attr('class', 'x-axis') .attr('transform', `translate(0,${innerHeight})`) .call(d3.axisBottom(xScale).ticks(6)); ``` **Axes with inner ticks:** ```javascript const tickSize = 6; gAxes.selectAll('.x-axis') .data([0]) .join('g') .attr('class', 'x-axis') .attr('transform', `translate(0,${innerHeight})`) .call(d3.axisBottom(xScale) .ticks(6) .tickSizeInner(-tickSize) // Negative = ticks point inward .tickSizeOuter(0)); // No outer ticks ``` **Custom shapes (5-point star):** ```javascript const starPath = (cx, cy, outerR, innerR) => { const points = []; for (let i = 0; i < 10; i++) { const r = i % 2 === 0 ? outerR : innerR; const angle = (Math.PI / 2) + (i * Math.PI / 5); points.push([cx + r * Math.cos(angle), cy - r * Math.sin(angle)]); } return 'M' + points.map(p => p.join(',')).join('L') + 'Z'; }; // Use with path elements gContent.selectAll('.point-star') .data(openModels) .join('path') .attr('d', d => starPath(xScale(d.x), yScale(d.y), radius * 1.2, radius * 0.5)) .attr('fill', d => d.color); ``` **Data-join for elements:** ```javascript gContent.selectAll('.point') .data(models) .join('circle') .attr('class', 'point') .attr('cx', d => xScale(d.x)) .attr('cy', d => yScale(d.y)) .attr('r', 8) .attr('fill', d => d.color) .on('mouseenter', showTooltip) .on('mousemove', showTooltip) .on('mouseleave', hideTooltip); ``` ## Step 4: Integrate in MDX In your `.mdx` file: ```mdx import HtmlEmbed from "../../../components/HtmlEmbed.astro";