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-key pattern already supports any field.
  • A second view — toggle between table and a CSS grid of cards.
  • Accessibility — add aria-sort to 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

The CollegeAthleteInsider Analyst

I'm an independent analyst covering college football and basketball through public data. Every number here traces to a script in /scripts. More about the methodology →