Menu
Public repository / master
AkurAI-Framework / site/frontend/boundaries-demo.html
Latest change 5fdd8b81e1f8244cbfc64e15d9ca2d5dba604bfb - fix: ensure boundaries.js loads before demo code runs by Ólafur Búi Ólafsson
{% include "header" %}
<main class="container">
<header class="page-head">
<span class="eyebrow">Live demo</span>
<h1>Loading & Error Boundaries</h1>
<p class="lede">Watch the top progress bar as you navigate between pages. Below, <code>withBoundary()</code> wraps async data fetches in graceful loading and error UI — no network failures break the page.</p>
</header>
<!-- SPA Navigation Demo -->
<section class="split">
<div>
<h2>Navigation Progress</h2>
<p>Click any link below. The progress bar at the top of the page shows the SPA navigation in progress. If the fetch fails, it falls back to a full-page navigation, keeping the user unstranded.</p>
<div class="btn-row" style="margin-top: 1.5rem;">
<a class="btn btn-primary" href="/">Home</a>
<a class="btn btn-secondary" href="/docs">Docs</a>
<a class="btn btn-secondary" href="/components">Components</a>
<a class="btn btn-ghost" href="/realtime">Realtime</a>
</div>
</div>
<div style="background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 2rem;">
<h3 style="margin-top: 0;">Console breadcrumbs</h3>
<p class="small muted">Open DevTools → Console to see <code>[boundaries]</code> log messages as you navigate:</p>
<ul class="small muted" style="margin: 1rem 0 0; padding-left: 1.5rem;">
<li><code>[boundaries] initialization</code></li>
<li><code>[boundaries] navigation start</code></li>
<li><code>[boundaries] navigation end</code></li>
</ul>
</div>
</section>
<!-- withBoundary Demo: Slow Fetch -->
<section style="margin: 4rem 0;">
<h2>Async Data with Loading Boundary</h2>
<p>Click "Load data" below. The boundary shows a spinner while fetching, then renders the content.</p>
<button class="btn btn-primary" id="demo-slow-load">Load data (slow)</button>
<div id="demo-slow-content" style="margin-top: 1.5rem;"></div>
</section>
<!-- withBoundary Demo: Error Handling -->
<section style="margin: 4rem 0;">
<h2>Error Boundary with Retry</h2>
<p>Click "Load and fail" below. The boundary catches the error, renders an error banner, and provides a Retry button.</p>
<button class="btn btn-primary" id="demo-error-load">Load and fail</button>
<div id="demo-error-content" style="margin-top: 1.5rem;"></div>
</section>
<!-- withBoundary Demo: Custom Loading (Skeleton) -->
<section style="margin: 4rem 0 6rem;">
<h2>Custom Loading UI</h2>
<p>This boundary shows a skeleton placeholder instead of a spinner, demonstrating how to customize loading states.</p>
<button class="btn btn-primary" id="demo-skeleton-load">Load with skeleton</button>
<div id="demo-skeleton-content" style="margin-top: 1.5rem;"></div>
</section>
<section class="container cta">
<h2>Ready to handle errors gracefully?</h2>
<div class="btn-row center">
<a class="btn btn-secondary" href="/docs/boundaries">Boundaries documentation</a>
<a class="btn btn-secondary" href="https://github.com/olibuijr/AkurAI-Framework">Source on GitHub</a>
</div>
</section>
</main>
<!-- Load boundaries.js before any demo code -->
<script type="module">
// Import boundaries.js to ensure it initializes before our demo code runs.
import "/boundaries.js" /*.then(() => { ... }) */;
// Guard on document.readyState to ensure DOM + boundaries.js are ready.
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initDemos);
} else {
initDemos();
}
function initDemos() {
// Demo 1: Slow fetch
var slowLoadBtn = document.getElementById("demo-slow-load");
var slowContent = document.getElementById("demo-slow-content");
if (slowLoadBtn && slowContent) {
slowLoadBtn.addEventListener("click", function () {
// Simulate a 2-second network delay.
window.SpaNavBoundary.withBoundary(
slowContent,
function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(
'<div class="card">' +
"<strong>Data loaded successfully!</strong>" +
"<p>This content was fetched asynchronously and rendered inside the boundary. " +
"The spinner appeared while loading, then vanished once the promise resolved.</p>" +
"</div>"
);
}, 2000);
});
},
{ loading: true, error: true }
);
});
}
// Demo 2: Error + Retry
var errorLoadBtn = document.getElementById("demo-error-load");
var errorContent = document.getElementById("demo-error-content");
if (errorLoadBtn && errorContent) {
errorLoadBtn.addEventListener("click", function () {
window.SpaNavBoundary.withBoundary(
errorContent,
function () {
return Promise.reject(new Error("Network request failed: 503 Service Unavailable"));
},
{ loading: true, error: true }
);
});
}
// Demo 3: Skeleton loading
var skeletonLoadBtn = document.getElementById("demo-skeleton-load");
var skeletonContent = document.getElementById("demo-skeleton-content");
if (skeletonLoadBtn && skeletonContent) {
skeletonLoadBtn.addEventListener("click", function () {
// Custom loading UI: show a skeleton card instead of a spinner.
skeletonContent.innerHTML =
'<div class="skeleton skeleton-card"></div>' +
'<div class="skeleton-line" style="margin-top: 1rem;"></div>' +
'<div class="skeleton-line skeleton-line-sm"></div>';
skeletonContent.setAttribute("aria-busy", "true");
// Fetch data after a delay.
Promise.resolve()
.then(function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(
'<div class="card">' +
"<strong>Skeleton replaced!</strong>" +
"<p>The skeleton loader was replaced with real content. " +
"You can customize loading UI per boundary by pre-populating the container.</p>" +
"</div>"
);
}, 1500);
});
})
.then(function (html) {
skeletonContent.innerHTML = html;
skeletonContent.removeAttribute("aria-busy");
console.log("[boundaries] skeleton content rendered");
})
.catch(function (err) {
skeletonContent.innerHTML =
'<div class="banner banner-danger">' +
'<span class="banner-icon">✕</span>' +
'<div class="banner-text"><strong>Failed</strong>' +
"<p style='margin:0.5rem 0 0;'>" +
err.message +
"</p></div>" +
"</div>";
skeletonContent.removeAttribute("aria-busy");
});
});
}
console.log("[boundaries-demo] initialization complete");
}
</script>
<!-- Load app.js last (after boundaries module import above) -->
<script type="module" src="/app.js"></script>
{% include "footer" %}