Digital Library
“Building an accessible digital reading experience with modular ES6 modules and declarative component injection—no build tools.”
A modular, zero-framework web reader and catalog platform featuring declarative HTML component injection and client-side search.
- Role
- Frontend Architect
- Context
- 3 Weeks (Semester 1 Web Project)
- Team
- Solo Project
- Core Stack
- HTML5, CSS Grid, Vanilla JS (ES6 Modules), Declarative Injection
Fig 1.0 — Architecture execution snapshot (Digital Library)
The Friction
Why build a multi-page digital library without a frontend framework or bundler?
Modern web development often introduces immense overhead for simple multi-page applications: megabytes of node_modules, webpack or vite build configurations, and complex hydration logic just to display static books and commentaries.
I wanted to build an elegant, fast-loading digital library using only vanilla web standards. The challenge was maintaining code modularity—sharing navigation bars, footers, and modals across dozens of book pages without copying and pasting HTML, and without relying on server-side rendering or heavy JavaScript single-page application frameworks.
Deliberate Constraints
The system architecture was not chosen in an unconstrained vacuum. Each structural decision emerged directly from four non-negotiable technical boundaries.
No React, Vue, Webpack, or npm build pipelines. 100% native browser execution.
Required native ES6 module imports (<script type='module'>) and custom declarative HTML injection.
Navigation, footers, and modal dialogs must be reusable without duplicating markup across pages.
Engineered a custom data-include engine that fetches and injects HTML component fragments asynchronously during DOMContentLoaded.
Extended reading requires comfortable line measure, accessible contrast, and zero layout shift.
Designed a modular CSS hierarchy with strict 65–75 character measure and persistent dark/light theme switching via localStorage.
Filtering hundreds of book entries must happen instantly without server queries or external search engines.
Built an in-memory search index that parses structured JSON records and updates DOM nodes with minimal reflow.
System Architecture & Data Pipeline
The application architecture is partitioned into two distinct JavaScript layers: a core foundation (/assets/js/core/) handling data fetching, declarative HTML component injection, and theme state; and a feature layer (/assets/js/features/) managing search, book grids, and modal previews.
Alto, Cultus, Corolla. Standard tiered rental base.
Audi A6, BMW 7, Land Cruiser. Chauffeur insurance rate.
Sportage, Tucson, Fortuner. All-terrain security deposit.
Bolan, Hiace, Coaster. High-capacity commercial rate.
Dynamic Polymorphism at Runtime: The orchestrator holds a single container std::vector<Vehicle*> fleet. When executing reservations or computing quotes, method calls to v->calculateCost(days) dynamically dispatch to the concrete subclass implementation through each instance's vtable pointer.
Subsystem Decomposition
Declarative HTML Inclusion Engine
assets/js/core/include.jsScans DOM for elements with data-include attributes and injects shared HTML snippets.
Central Data Caching Layer
assets/js/core/data.jsLoads and caches book catalog records from assets/data/books.json.
Real-Time Search & Filter Engine
assets/js/features/search.jsFilters the active catalog by title, author, and category with debounced keystroke listeners.
Persistent Theme & Modal Manager
assets/js/core/theme.js & book.jsManages dark/light theme persistence in localStorage and orchestrates 'Quick Peek' book preview modals.
The Hard Part: Asynchronous HTML Injection & Event Binding Race Conditions
How injecting HTML components dynamically breaks traditional DOM event listeners.
When breaking down pages to use reusable navigation and search bars, page-specific feature scripts (like search.js and theme.js) began throwing 'null element' errors on cold loads.
Because fetch() for data-include snippets is asynchronous, feature scripts running on DOMContentLoaded were attempting to attach event listeners to elements (such as the mobile menu toggle or search input) that had not yet been injected into the DOM tree.
// core/include.js - Asynchronous declarative injection
export async function loadComponents() {
const elements = document.querySelectorAll('[data-include]');
const promises = Array.from(elements).map(async (el) => {
const file = el.getAttribute('data-include');
const response = await fetch(file);
const html = await response.text();
el.outerHTML = html;
});
// CRITICAL: Await all component injections before resolving
await Promise.all(promises);
}
// app.js - Orchestrated Bootstrap
document.addEventListener('DOMContentLoaded', async () => {
// 1. First await complete component injection
await loadComponents();
// 2. Safely initialize features now that DOM nodes exist
initTheme();
initSearch();
initBooksGrid();
});We redesigned include.js to return a unified Promise.all() containing all injection fetch operations, and re-architected app.js as an asynchronous lifecycle bootstrapper that halts feature initialization until all injected DOM fragments are settled.
Understanding the browser event loop and asynchronous DOM mutations is fundamental. When you don't have a virtual DOM framework to hide the timing, you learn how the browser actually schedules layout, script execution, and rendering.
Lighthouse Performance & Asset Audit
Audit results demonstrating sub-second load times and zero render-blocking JavaScript dependencies.
Engineering Reflection
“Simplicity in web architecture is not the absence of ambition; it is the discipline to use the platform as it was intended.”
In the modern frontend landscape, developers frequently default to multi-megabyte toolchains before writing a single line of application logic. We create build problems, and then invent complex tools to solve the build problems.
Building this digital library proved that standard HTML, modular CSS, and modern ES6 modules provide everything needed for a fast, responsive, and maintainable web application.
The site loads in a fraction of a second, requires no compilation step, and can be hosted on any static web server in the world without maintenance.
Interested in discussing this architecture?
I'm always open to technical dialogue, code reviews, and exploring system constraints.