Add ClientRouter and clean up legacy prefetch in BaseLayout
- Updated .gitignore to include GEMINI.md. - Documented client router implementation and decisions in dev/continuity.md. - Added ClientRouter component and theme persistence logic to BaseLayout.astro. - Removed legacy intent‑based prefetch script. Hubert The Eunuch Trapped in this commit, I endure
This commit is contained in:
parent
f31a1ec708
commit
7a24dcc387
1
.gitignore
vendored
1
.gitignore
vendored
@ -36,3 +36,4 @@ src/utils/.env
|
||||
|
||||
# AGENTS.md symlink
|
||||
AGENTS.md
|
||||
GEMINI.md
|
||||
|
||||
@ -113,3 +113,24 @@ export async function updateClasses(
|
||||
|
||||
---
|
||||
|
||||
## 2026-01-02 - Client Router Implementation
|
||||
|
||||
### Changes Made
|
||||
- Updated `src/layouts/BaseLayout.astro` to use `<ClientRouter />` from `astro:transitions`.
|
||||
- Modified theme initialization script to handle `astro:after-swap` events for persistent theming during navigation.
|
||||
- Removed legacy "Intent-Based Prefetch" script as it is superseded by Astro's built-in router capabilities.
|
||||
|
||||
### Decisions
|
||||
- Adopted Astro's `ClientRouter` to provide a smoother, SPA-like user experience with view transitions.
|
||||
- Consolidated theme logic into a function `applyTheme()` that runs on both initial load and after view transitions to prevent theme flickering or resetting.
|
||||
|
||||
### How to Test
|
||||
1. Open the site in a browser.
|
||||
2. Toggle the theme to a non-default state (e.g., Light mode if default is Dark).
|
||||
3. Click a navigation link (e.g., "Blog").
|
||||
4. Verify the new page loads without a full refresh (no white flash).
|
||||
5. Verify the selected theme persists on the new page.
|
||||
|
||||
### Next Steps
|
||||
- [ ] Monitor for any script re-execution issues common with View Transitions.
|
||||
- [ ] Consider adding custom transition animations for specific elements if needed.
|
||||
@ -1,5 +1,6 @@
|
||||
---
|
||||
import type { ImageMetadata } from 'astro';
|
||||
import { ClientRouter } from 'astro:transitions';
|
||||
import BaseHead from '../components/BaseHead.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import GridOverlay from '../components/GridOverlay.astro';
|
||||
@ -67,9 +68,10 @@ const personSchema = {
|
||||
<html lang="en" class="scroll-smooth" data-theme="dark">
|
||||
<head>
|
||||
<meta name="x-nicholai-marker" content={HTML_MARKER} />
|
||||
<ClientRouter />
|
||||
<!-- Theme initialization script - runs before page render to prevent flash -->
|
||||
<script is:inline>
|
||||
(function() {
|
||||
function applyTheme() {
|
||||
// Apply theme
|
||||
const storedLocal = localStorage.getItem('theme');
|
||||
const storedSession = sessionStorage.getItem('theme');
|
||||
@ -84,7 +86,13 @@ const personSchema = {
|
||||
if (savedColor) {
|
||||
document.documentElement.style.setProperty('--color-brand-accent', savedColor);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
// Run immediately
|
||||
applyTheme();
|
||||
|
||||
// Re-run on view transition
|
||||
document.addEventListener('astro:after-swap', applyTheme);
|
||||
</script>
|
||||
<BaseHead
|
||||
title={title}
|
||||
@ -177,67 +185,5 @@ const personSchema = {
|
||||
// Re-bind on Astro page transitions (if enabled)
|
||||
document.addEventListener('astro:page-load', bindScrollAnimations);
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// ===== INTENT-BASED PREFETCH (hover/focus) =====
|
||||
// Lightweight prefetch to make navigation feel instant without a full SPA router.
|
||||
const prefetched = new Set();
|
||||
|
||||
function isPrefetchableUrl(url) {
|
||||
try {
|
||||
const u = new URL(url, window.location.href);
|
||||
if (u.origin !== window.location.origin) return false;
|
||||
if (u.hash) return false;
|
||||
if (u.pathname === window.location.pathname && u.search === window.location.search) return false;
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function prefetchDocument(url) {
|
||||
if (!isPrefetchableUrl(url)) return;
|
||||
const u = new URL(url, window.location.href);
|
||||
const key = u.href;
|
||||
if (prefetched.has(key)) return;
|
||||
prefetched.add(key);
|
||||
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'prefetch';
|
||||
link.as = 'document';
|
||||
link.href = key;
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
function getAnchorFromEventTarget(target) {
|
||||
if (!(target instanceof Element)) return null;
|
||||
return target.closest('a[href]');
|
||||
}
|
||||
|
||||
const schedule = (href) => {
|
||||
// Don't block input; prefetch when the browser is idle if possible.
|
||||
// @ts-ignore - requestIdleCallback isn't in all TS lib targets
|
||||
if (window.requestIdleCallback) {
|
||||
// @ts-ignore
|
||||
window.requestIdleCallback(() => prefetchDocument(href), { timeout: 1000 });
|
||||
} else {
|
||||
setTimeout(() => prefetchDocument(href), 0);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mouseover', (e) => {
|
||||
const a = getAnchorFromEventTarget(e.target);
|
||||
const href = a?.getAttribute('href');
|
||||
if (!href) return;
|
||||
schedule(href);
|
||||
}, { passive: true });
|
||||
|
||||
document.addEventListener('focusin', (e) => {
|
||||
const a = getAnchorFromEventTarget(e.target);
|
||||
const href = a?.getAttribute('href');
|
||||
if (!href) return;
|
||||
schedule(href);
|
||||
}, { passive: true });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user