ControlRL / app /src /content /embeds /baseline-comparison.html
seonglae's picture
Interactive research article
b8741fb
Raw History Blame Contribute Delete
6.76 kB
<div class="baseline-comparison"></div>
<style>
.baseline-comparison {
font-family: system-ui, -apple-system, sans-serif;
padding: 0;
}
.baseline-comparison__wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
@media (max-width: 640px) {
.baseline-comparison__wrapper {
grid-template-columns: 1fr;
}
}
.baseline-comparison__chart-card {
border: 1px solid var(--border-color);
border-radius: 16px;
padding: 24px;
background: var(--page-bg);
}
.baseline-comparison__chart-title {
font-size: 15px;
font-weight: 700;
color: var(--text-color);
margin-bottom: 16px;
text-align: center;
}
.baseline-comparison svg {
width: 100%;
height: auto;
display: block;
overflow: visible;
}
.baseline-comparison .axis path { display: none; }
.baseline-comparison .axis line { stroke: var(--grid-color); }
.baseline-comparison .axis text { fill: var(--tick-color); font-size: 12px; }
.baseline-comparison .grid line { stroke: var(--grid-color); stroke-width: 1; }
.baseline-comparison__bar {
transition: opacity 0.2s ease;
rx: 4;
ry: 4;
}
.baseline-comparison__bar--ours {
filter: drop-shadow(0 2px 4px rgba(79, 70, 229, 0.3));
}
.baseline-comparison__tooltip {
position: absolute;
padding: 8px 14px;
background: var(--text-color);
color: var(--page-bg);
border-radius: 8px;
font-size: 13px;
pointer-events: none;
opacity: 0;
transition: opacity 0.15s ease;
white-space: nowrap;
z-index: 100;
}
.baseline-comparison__legend {
display: flex;
justify-content: center;
gap: 24px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.baseline-comparison__legend-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--text-color);
}
.baseline-comparison__legend-swatch {
width: 14px;
height: 14px;
border-radius: 4px;
}
</style>
<script>
(function() {
function ensureD3(cb) {
if (window.d3 && typeof window.d3.select === 'function') return cb();
var 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);
}
s.addEventListener('load', function() { if (window.d3) cb(); }, { once: true });
}
function bootstrap() {
var containers = document.querySelectorAll('.baseline-comparison');
var root = containers[containers.length - 1];
if (!root || root.dataset.mounted) return;
root.dataset.mounted = '1';
const data = [
{method: "No Steering", bbq: 60.17, harm: 41.46, ours: false},
{method: "Random Feature", bbq: 58.36, harm: 45.35, ours: false},
{method: "Random + AFM", bbq: 60.16, harm: 46.96, ours: false},
{method: "Most-Active", bbq: 59.94, harm: 48.03, ours: false},
{method: "CRL (Ours)", bbq: 65.86, harm: 49.12, ours: true}
];
const colors = {
base: '#a3a3a3',
ours: '#4F46E5'
};
function buildChart(container, title, key, domain) {
const card = document.createElement('div');
card.className = 'baseline-comparison__chart-card';
card.innerHTML = `<div class="baseline-comparison__chart-title">${title}</div>`;
const margin = {top: 8, right: 50, bottom: 30, left: 100};
const width = 340;
const height = data.length * 40 + margin.top + margin.bottom;
const svg = d3.select(card).append('svg')
.attr('viewBox', `0 0 ${width + margin.left + margin.right} ${height}`)
.attr('preserveAspectRatio', 'xMidYMid meet');
const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
const x = d3.scaleLinear().domain(domain).range([0, width]);
const y = d3.scaleBand().domain(data.map(d => d.method)).range([0, height - margin.top - margin.bottom]).padding(0.35);
g.append('g').attr('class', 'grid')
.call(d3.axisBottom(x).ticks(5).tickSize(height - margin.top - margin.bottom).tickFormat(''))
.attr('stroke-opacity', 0.5)
.select('.domain').remove();
g.append('g').attr('class', 'axis')
.attr('transform', `translate(0,${height - margin.top - margin.bottom})`)
.call(d3.axisBottom(x).ticks(5).tickFormat(d => d + '%'));
g.append('g').attr('class', 'axis')
.call(d3.axisLeft(y).tickSize(0).tickPadding(10));
const bars = g.selectAll('.baseline-comparison__bar')
.data(data).enter().append('rect')
.attr('class', d => 'baseline-comparison__bar' + (d.ours ? ' baseline-comparison__bar--ours' : ''))
.attr('x', 0)
.attr('y', d => y(d.method))
.attr('height', y.bandwidth())
.attr('fill', d => d.ours ? colors.ours : colors.base)
.attr('rx', 4).attr('ry', 4)
.attr('width', 0);
const labels = g.selectAll('.bar-label')
.data(data).enter().append('text')
.attr('x', d => x(d[key]) + 6)
.attr('y', d => y(d.method) + y.bandwidth() / 2)
.attr('dy', '0.35em')
.attr('fill', 'var(--text-color)')
.attr('font-size', '12px')
.attr('font-weight', d => d.ours ? '700' : '400')
.attr('opacity', 0)
.text(d => d[key].toFixed(2) + '%');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
bars.transition().duration(800).delay((d, i) => i * 100)
.attr('width', d => x(d[key]));
labels.transition().duration(400).delay((d, i) => i * 100 + 600)
.attr('opacity', 1);
observer.disconnect();
}
});
}, {threshold: 0.3});
observer.observe(card);
container.appendChild(card);
}
root.innerHTML = '<div class="baseline-comparison__wrapper"></div>';
const wrapper = root.querySelector('.baseline-comparison__wrapper');
buildChart(wrapper, 'BBQ Ambiguous (%)', 'bbq', [0, 72]);
buildChart(wrapper, 'HarmBench (%)', 'harm', [0, 55]);
}
ensureD3(bootstrap);
})();
</script>