remi.works
Technical Overview
remi.works is a static, content-driven portfolio system deployed on GitHub Pages. The site is intentionally built with
plain HTML, CSS, and modular JavaScript so pages remain lightweight, inspectable, and easy to ship, while still supporting richer
behavior such as animated navigation, theming, search, and interactive canvas demos.
Architecture
- Static routes define the core surface area:
index.html,blog.html,projects.html,project.html, and related pages. - Structured metadata lives in JSON indexes:
data/posts.json,data/projects.json, anddata/resume.json. - Long-form content is stored as HTML fragments in
blog/posts/*.htmlandprojects/details/*.html. - Page responsibilities are split into focused modules under
js/*.jsto keep logic isolated and reusable.
Content Pipeline
Blog posts and projects are loaded from indexed metadata and resolved by slug at runtime. The loader memoizes successful responses and coalesces in-flight requests, which reduces duplicate network work and keeps rendering deterministic across navigation paths.
async function loadProjects() {
if (Array.isArray(projectsCache)) {
return projectsCache.slice(0);
}
if (projectsRequest) {
var pending = await projectsRequest;
return pending.slice(0);
}
projectsRequest = fetch(PROJECTS_URL, { cache: "no-store" })
.then(function (response) {
if (!response.ok) {
throw new Error("Failed to load projects index");
}
return response.json();
})
.then(function (items) {
projectsCache = normalizeProjects(items);
return projectsCache;
})
.finally(function () {
projectsRequest = null;
});
var loaded = await projectsRequest;
return loaded.slice(0);
}
Theme Runtime
The theme system maps color pairs into CSS custom properties and persists user choice in session storage. This allows instant palette updates and contrast-aware styling without rebuilding the DOM or forcing a full page refresh.
function applyPair(pair) {
var root = document.documentElement;
root.style.setProperty("--color-1", rgbToHex(pair.primary));
root.style.setProperty("--color-2", rgbToHex(pair.secondary));
root.style.setProperty("--color-1-rgb", pair.primary.r + ", " + pair.primary.g + ", " + pair.primary.b);
root.style.setProperty("--color-2-rgb", pair.secondary.r + ", " + pair.secondary.g + ", " + pair.secondary.b);
root.style.setProperty("--theme-contrast", pair.ratio.toFixed(2));
}
function setToggleSymbol(button, mode) {
button.textContent = mode === "night" ? "☾\uFE0E" : "☀\uFE0E";
button.setAttribute("aria-pressed", mode === "night" ? "true" : "false");
}
Navigation Model
Navigation uses an MPA shell with selective in-page replacement. Internal links are intercepted, the destination document is
fetched, and only the <main> subtree is swapped while shared chrome remains mounted. This preserves context and
enables smoother page-to-page transitions without introducing a full SPA framework.
function navigateTo(url, options) {
var targetUrl = new URL(url, window.location.href);
return fetch(targetUrl.href, {
credentials: "same-origin",
headers: { "X-Requested-With": "fetch" }
})
.then(function (response) {
if (!response.ok) {
throw new Error("Failed to fetch page");
}
return response.text();
})
.then(function (html) {
var parser = new DOMParser();
var nextDoc = parser.parseFromString(html, "text/html");
var nextMain = nextDoc.querySelector("main");
var currentMain = document.querySelector("main");
currentMain.replaceWith(nextMain);
updateHead(nextDoc);
updateNavCurrent(targetUrl);
});
}
Unified Search
Search spans blog, project, and resume data through a shared normalization path. Text is lowercased, de-accented, and tokenized so matching behavior is consistent even when source documents come from different schemas and writing styles.
function normalizeText(value) {
var text = String(value || "").toLowerCase();
if (typeof text.normalize === "function") {
text = text.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}
return text
.replace(/[^a-z0-9\s]/g, " ")
.replace(/\s+/g, " ")
.trim();
}
Interactive Canvas System
The homepage boids demo uses a fixed-step simulation loop with interpolated rendering. Physics updates run at a stable cadence, while draw calls interpolate between states for smoother animation under variable frame times. Boid silhouettes are defined by constant geometry values so the visual language stays flat and intentionally 2D across devices.
var BOID_HEAD_LENGTH = 5.8;
var BOID_TAIL_LENGTH = 3.5;
var BOID_WING_OFFSET = 2.5;
function tick(ts) {
var simStepMs = lowPowerMode ? lowPowerSimStepMs : normalSimStepMs;
accumulatorMs += deltaMs;
while (accumulatorMs >= simStepMs && steps < maxStepsThisFrame) {
stepSimulation(simStepMs);
accumulatorMs -= simStepMs;
steps += 1;
}
renderFrame(simStepMs > 0 ? accumulatorMs / simStepMs : 1);
}
Deployment
Delivery is handled by GitHub Pages. The repository publishes as a static artifact, so deployment is branch-driven and operational overhead stays low: no app server, no process manager, and no runtime infrastructure to maintain.
Related Reading
For launch context and rationale, read the companion blog post: Hello World!