Clémentine
ongoing rewriting, first fig working
ad0a935
raw
history blame
5.79 kB
<div class="d3-intro-boxes"></div>
<style>
.d3-intro-boxes {
position: relative;
width: 100%;
min-height: 250px;
overflow: visible;
}
.d3-intro-boxes svg {
display: block;
width: 100%;
height: auto;
}
.d3-intro-boxes .box-title {
font-size: 16px;
font-weight: 600;
fill: var(--text-color);
}
.d3-intro-boxes .box-item {
font-size: 13px;
fill: var(--text-color);
}
.d3-intro-boxes .box-rect {
fill: var(--surface-bg);
stroke-width: 3;
}
</style>
<script>
(() => {
const ensureD3 = (cb) => {
if (window.d3 && typeof window.d3.select === 'function') return cb();
let s = document.getElementById('d3-cdn-script');
if (!s) {
s = document.createElement('script');
s.id = 'd3-cdn-script';
s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js';
document.head.appendChild(s);
}
const onReady = () => {
if (window.d3 && typeof window.d3.select === 'function') cb();
};
s.addEventListener('load', onReady, { once: true });
if (window.d3) onReady();
};
const bootstrap = () => {
const scriptEl = document.currentScript;
let container = scriptEl ? scriptEl.previousElementSibling : null;
if (!(container && container.classList && container.classList.contains('d3-intro-boxes'))) {
const candidates = Array.from(document.querySelectorAll('.d3-intro-boxes'))
.filter((el) => !(el.dataset && el.dataset.mounted === 'true'));
container = candidates[candidates.length - 1] || null;
}
if (!container) return;
if (container.dataset) {
if (container.dataset.mounted === 'true') return;
container.dataset.mounted = 'true';
}
// Data for the three boxes
const data = [
{
title: "Model builders",
items: ["best training method", "non-regression", "risks/costs"],
colorKey: 0
},
{
title: "Users",
items: ["best model for X", "hype vs trust"],
colorKey: 1
},
{
title: "Field",
items: ["capabilities", "direction"],
colorKey: 2,
hasIcon: true
}
];
// Get colors - orange gradient as shown in the image
const getColors = () => {
// Use the exact colors from the image
return ['#FFA500', '#FFD700', '#FFB347'];
};
const colors = getColors();
// Create SVG
const svg = d3.select(container).append('svg');
const gRoot = svg.append('g');
let width = 800;
let height = 250;
const boxWidth = 180;
const boxHeight = 160;
const boxSpacing = 30;
const margin = { top: 20, right: 30, bottom: 20, left: 30 };
function updateSize() {
width = container.clientWidth || 800;
// Fixed height to ensure content fits
height = 250;
svg.attr('width', width).attr('height', height);
return { width, height };
}
function render() {
const { width: w, height: h } = updateSize();
// Calculate total width needed for boxes
const totalBoxWidth = data.length * boxWidth + (data.length - 1) * boxSpacing;
// Add margins to ensure boxes don't get cut off
const availableWidth = w - margin.left - margin.right;
const scale = Math.min(1, availableWidth / totalBoxWidth);
const scaledBoxWidth = boxWidth * scale;
const scaledBoxSpacing = boxSpacing * scale;
const scaledTotalWidth = data.length * scaledBoxWidth + (data.length - 1) * scaledBoxSpacing;
const startX = margin.left + (availableWidth - scaledTotalWidth) / 2;
// Center boxes vertically with proper padding
const startY = (h - boxHeight) / 2;
// Clear and redraw
gRoot.selectAll('*').remove();
const boxes = gRoot.selectAll('g.box')
.data(data)
.join('g')
.attr('class', 'box')
.attr('transform', (d, i) => `translate(${startX + i * (scaledBoxWidth + scaledBoxSpacing)}, ${startY})`);
// Draw rounded rectangles
boxes.append('rect')
.attr('class', 'box-rect')
.attr('width', scaledBoxWidth)
.attr('height', boxHeight)
.attr('rx', 20)
.attr('ry', 20)
.attr('stroke', (d, i) => colors[d.colorKey]);
// Add titles
boxes.append('text')
.attr('class', 'box-title')
.attr('x', scaledBoxWidth / 2)
.attr('y', 35)
.attr('text-anchor', 'middle')
.text(d => d.title);
// Add list items
boxes.each(function(d) {
const box = d3.select(this);
d.items.forEach((item, i) => {
box.append('text')
.attr('class', 'box-item')
.attr('x', 15)
.attr('y', 65 + i * 22)
.text(`- ${item}`);
});
// Add icon for Field box
if (d.hasIcon) {
box.append('text')
.attr('x', scaledBoxWidth - 35)
.attr('y', boxHeight - 20)
.attr('font-size', '28px')
.text('🔄');
}
});
}
// Initial render
render();
// Resize handling
const rerender = () => render();
if (window.ResizeObserver) {
const ro = new ResizeObserver(() => rerender());
ro.observe(container);
} else {
window.addEventListener('resize', rerender);
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => ensureD3(bootstrap), { once: true });
} else {
ensureD3(bootstrap);
}
})();
</script>