You don't need React, a build step, or a server to ship an interactive stats dashboard. A single HTML file, a little CSS, and a few dozen lines of vanilla JavaScript will read a JSON file and render a sortable, filterable table with inline bar charts — and it'll load instantly and deploy anywhere. This is the exact philosophy behind this whole site. Let's build one.
Step 1: the data file
Export your analysis (from any of the Python tutorials) to a JSON array — one object per team:
// teams.json
[
{"team": "Duke", "adjEM": 47.6, "adjO": 132.9, "adjD": 85.3},
{"team": "Houston", "adjEM": 45.2, "adjO": 126.5, "adjD": 81.3},
{"team": "Auburn", "adjEM": 43.2, "adjO": 130.9, "adjD": 87.8}
]
Your Python script writes this; the dashboard just reads it.
Step 2: the HTML skeleton
<input id="q" type="search" placeholder="Filter teams…">
<table>
<thead><tr><th data-key="team">Team</th><th data-key="adjEM">AdjEM</th></tr></thead>
<tbody id="rows"></tbody>
</table>
Step 3: fetch, render, sort, filter
The whole brain of the dashboard is one fetch and a render function:
let data = [], sortKey = "adjEM", asc = false;
const rows = document.getElementById("rows");
fetch("teams.json").then(r => r.json()).then(d => { data = d; render(); });
function render() {
const q = document.getElementById("q").value.toLowerCase();
const max = Math.max(...data.map(t => t.adjEM));
const view = data
.filter(t => t.team.toLowerCase().includes(q))
.sort((a, b) => (asc ? 1 : -1) * (a[sortKey] > b[sortKey] ? 1 : -1));
rows.innerHTML = view.map(t => `
<tr><td>${t.team}</td>
<td><span class="bar" style="width:${t.adjEM / max * 100}%"></span>${t.adjEM}</td></tr>`).join("");
}
document.getElementById("q").addEventListener("input", render);
document.querySelectorAll("th").forEach(th => th.addEventListener("click", () => {
const k = th.dataset.key; asc = (k === sortKey) ? !asc : false; sortKey = k; render();
}));
Click a header to sort; type to filter. No framework, no build.
Step 4: inline bar charts with pure CSS
That <span class="bar"> becomes a chart with three lines of CSS — set its width as a percentage of the max and give it a background:
.bar { display: inline-block; height: 0.7em; margin-right: 6px;
background: #b0892f; border-radius: 2px; vertical-align: middle; }
Now each row shows a gold bar proportional to the value — a sortable bar chart with zero charting libraries.
Why build it this way
- It's instant. No framework to download, no hydration. The page is interactive the moment it loads.
- It deploys anywhere. Drop the HTML and JSON on any static host (Netlify, Cloudflare Pages, GitHub Pages) — the same way this site deploys.
- It's maintainable. Re-run your Python, overwrite
teams.json, and the dashboard updates. Data and presentation are cleanly separated. - It degrades gracefully. Add a static
<tbody>fallback for no-JS visitors, exactly like this site's article grid.
Extend it
- Multiple sortable columns — the
data-keypattern already supports any field. - A second view — toggle between table and a CSS grid of cards.
- Accessibility — add
aria-sortto the active header and keyboard handlers, as our site's search does.
The lesson generalizes: for read-only data tools, plain HTML/CSS/JS is often the right choice, not a compromise. It's faster to build, faster to load, and trivial to host.
Sources & further reading
- MDN Web Docs — developer.mozilla.org (fetch, the DOM)
- This site's own
js/search.jsuses the same fetch-and-render pattern. - Related: Generate the JSON with pandas · Compute the ratings to display