site: rebuild landing page at 2026-04-30T12:33:24Z

This commit is contained in:
Alan Wizemann
2026-04-30 14:33:24 +02:00
parent abdc031d5b
commit d3b8971a39
2 changed files with 58 additions and 14 deletions
+51 -7
View File
@@ -11,16 +11,50 @@
} else {
root.removeAttribute('data-theme');
}
applyImageTheme();
}
// Hydrate stored preference (if any) — runs after DOMContentLoaded since the
// <script> is deferred. There's a brief moment of media-query default before
// hydrate; that's acceptable here (no FOUC because the media query already
// gets the right colors).
// Resolve the *effective* theme — explicit data-theme wins, otherwise
// fall back to the OS preference.
function resolveTheme() {
const explicit = root.getAttribute('data-theme');
if (explicit === 'light' || explicit === 'dark') return explicit;
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
// Swap every <img data-dark-src="..."> between its light and dark variants.
// Also rewrites the parent <picture>'s <source srcset> so the picture
// algorithm doesn't override us on resize/layout passes.
function applyImageTheme() {
const theme = resolveTheme();
document.querySelectorAll('img[data-dark-src]').forEach((img) => {
if (!img.dataset.lightSrc) {
img.dataset.lightSrc = img.getAttribute('src');
}
const target = theme === 'dark' ? img.dataset.darkSrc : img.dataset.lightSrc;
if (img.getAttribute('src') !== target) img.setAttribute('src', target);
const picture = img.parentElement;
if (picture && picture.tagName === 'PICTURE') {
picture.querySelectorAll('source').forEach((s) => {
if (s.getAttribute('srcset') !== target) s.setAttribute('srcset', target);
});
}
});
}
// Hydrate stored preference (if any) — runs after DOMContentLoaded since
// the <script> is deferred. There's a brief moment of media-query default
// before hydrate; that's acceptable here (no FOUC because the media query
// already gets the right colors and the first images render at light by
// default — JS swaps within a frame on dark-mode systems).
let stored = null;
try {
const stored = localStorage.getItem(STORAGE_KEY);
stored = localStorage.getItem(STORAGE_KEY);
if (stored === 'light' || stored === 'dark') applyTheme(stored);
} catch (_) { /* localStorage unavailable — fall back to media query */ }
else applyImageTheme(); // initial pass even if no stored preference
} catch (_) {
applyImageTheme();
}
const toggle = document.querySelector('[data-theme-toggle]');
if (toggle) {
@@ -40,8 +74,18 @@
});
}
// Re-apply on system preference change so users who haven't set an
// explicit override still get matching screenshots.
if (window.matchMedia) {
const mql = window.matchMedia('(prefers-color-scheme: dark)');
const onChange = () => {
if (!root.hasAttribute('data-theme')) applyImageTheme();
};
if (mql.addEventListener) mql.addEventListener('change', onChange);
else if (mql.addListener) mql.addListener(onChange);
}
// Auto-collapse sticky header on scroll-down, restore on scroll-up.
// Pure progressive enhancement — no header in the DOM = nothing happens.
const header = document.querySelector('.site-header');
if (header) {
let lastY = window.scrollY;
+7 -7
View File
@@ -91,7 +91,7 @@
<div class="hero-visual" aria-hidden="false">
<picture class="hero-mac">
<source media="(prefers-color-scheme: dark)" srcset="assets/screenshots/mac-hero-dark.png">
<img src="assets/screenshots/mac-hero.png"
<img data-dark-src="assets/screenshots/mac-hero-dark.png" src="assets/screenshots/mac-hero.png"
alt="Scarf on macOS — chat view streaming a response with a tool-call card"
width="1600" height="1000"
fetchpriority="high" decoding="async">
@@ -148,7 +148,7 @@
<div class="feature-visual">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/screenshots/mac-chat-dark.png">
<img src="assets/screenshots/mac-chat.png"
<img data-dark-src="assets/screenshots/mac-chat-dark.png" src="assets/screenshots/mac-chat.png"
alt="Mac app — chat view with a tool-call card and a streaming response"
width="1600" height="1000" loading="lazy" decoding="async">
</picture>
@@ -169,7 +169,7 @@
<div class="feature-visual">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/screenshots/mac-projects-dark.png">
<img src="assets/screenshots/mac-projects.png"
<img data-dark-src="assets/screenshots/mac-projects-dark.png" src="assets/screenshots/mac-projects.png"
alt="Mac app — project dashboard with stat boxes and a chart widget"
width="1600" height="1000" loading="lazy" decoding="async">
</picture>
@@ -191,7 +191,7 @@
<div class="feature-visual">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/screenshots/mac-sessions-dark.png">
<img src="assets/screenshots/mac-sessions.png"
<img data-dark-src="assets/screenshots/mac-sessions-dark.png" src="assets/screenshots/mac-sessions.png"
alt="Mac app — sessions browser filtered to a remote server"
width="1600" height="1000" loading="lazy" decoding="async">
</picture>
@@ -212,7 +212,7 @@
<div class="feature-visual">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/screenshots/mac-mcp-dark.png">
<img src="assets/screenshots/mac-mcp.png"
<img data-dark-src="assets/screenshots/mac-mcp-dark.png" src="assets/screenshots/mac-mcp.png"
alt="Mac app — MCP servers configuration view"
width="1600" height="1000" loading="lazy" decoding="async">
</picture>
@@ -233,7 +233,7 @@
<div class="feature-visual">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/screenshots/mac-cron-dark.png">
<img src="assets/screenshots/mac-cron.png"
<img data-dark-src="assets/screenshots/mac-cron-dark.png" src="assets/screenshots/mac-cron.png"
alt="Mac app — cron manager with paused and active jobs"
width="1600" height="1000" loading="lazy" decoding="async">
</picture>
@@ -254,7 +254,7 @@
<div class="feature-visual">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/screenshots/mac-dashboard-dark.png">
<img src="assets/screenshots/mac-dashboard.png"
<img data-dark-src="assets/screenshots/mac-dashboard-dark.png" src="assets/screenshots/mac-dashboard.png"
alt="Mac app — dashboard with token usage, recent sessions, and health"
width="1600" height="1000" loading="lazy" decoding="async">
</picture>