Every report started as a standalone HTML file at /research/some-report/ with its own layout. Click a link, get a full page refresh. Fine for a few pages, but frustrating when flipping between them.
I wanted a single-page feel without rebuilding in a framework.
The hybrid approach
Reports still live at src/research/<slug>/index.html with frontmatter, so Eleventy collects them and their metadata. But the layout now redirects instantly:
<script>window.location.replace('/research/#research-page-hybrid-approach');</script>
Land on /research/excel-minis/ and you're sent to /research/#excel-minis. The report page exists as a fetch target, not a destination.
Click a sidebar link on the research page and vanilla JS fetches the report, extracts .blog-content, and swaps it in:
fetch(url)
.then(res => res.text())
.then(html => {
const doc = new DOMParser().parseFromString(html, 'text/html');
const blogContent = doc.querySelector('.blog-content');
if (blogContent) {
blogContent.querySelectorAll('img, iframe').forEach(el => {
const src = el.getAttribute('src');
if (src && src.startsWith('./'))
el.setAttribute('src', url + src.slice(1));
});
content.innerHTML = blogContent.outerHTML;
}
});
Fetch, parse, swap. No router, no state management, no virtual DOM.
Why vanilla JS
The requirements were small: fetch content, fix relative paths, update the URL hash, highlight the sidebar. About 50 lines of logic. Adding a framework would have been more complexity than the problem itself.
Downside: no loading spinners or transitions. Click a link, see "Loading...", then the content. Fast enough that it doesn't matter.
Deep linking for free
The URL hash maps to the report slug (#excel-minis, etc.), so sharing a link lands you on the exact report with no extra routing. The redirect ties old standalone URLs into the same system.
Future reports
New reports are just src/research/<slug>/index.html with frontmatter. Passthrough copy handles images and downloads. The sidebar populates automatically from the Eleventy collection. No JS changes needed.
The hybrid gives me build-time metadata from a static site generator with a lightweight SPA feel. More reports won't slow anything down -- it's just fetching text and swapping a div.