Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# CHANGELOG.md

## v0.47.0 (unreleased)
- Added a responsive, keyboard-accessible comparison of SQLPage and five alternative approaches to the official homepage.
- The official homepage's database sculpture now scrolls natively with its section after the opening flight, and section layouts reserve its space with CSS grid.
- For sites that store their pages in a database, checking for page updates no longer makes other pages wait when the database is busy. In a test, another page loaded in under 1 ms instead of about 250 ms.
- Redesigned the official website homepage with live SQLPage examples, an interactive database sculpture, flowing section transitions, responsive layouts, and direct links to documentation and deployment guides.
Expand Down
69 changes: 69 additions & 0 deletions examples/official-site/assets/landing/js/comparison.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/** Native scrolling remains available without enhancement, including on touch screens. */
export function initComparison(root, signal) {
const section = root.querySelector(".comparison-section");
const track = section.querySelector(".comparison-track");
const cards = [...track.children];
const previous = section.querySelector("[data-comparison-previous]");
const next = section.querySelector("[data-comparison-next]");
const status = section.querySelector("[data-comparison-status]");
const motion = matchMedia("(prefers-reduced-motion: reduce)");

function update() {
const offsets = cards.map((card) => card.offsetLeft);
const first = offsets.findIndex(
(left, index) => left + cards[index].offsetWidth > track.scrollLeft + 2,
);
const last = offsets.findLastIndex(
(left) => left < track.scrollLeft + track.clientWidth - 2,
);
previous.disabled = track.scrollLeft <= 2;
next.disabled =
track.scrollLeft >= track.scrollWidth - track.clientWidth - 2;
const range =
first === last ? String(first + 1) : `${first + 1}–${last + 1}`;
const label = `${range} of ${cards.length} alternatives`;
// Avoid repeating live announcements during every frame of a smooth scroll.
if (status.textContent !== label) status.textContent = label;
}

function goTo(left) {
track.scrollTo({
left,
behavior: motion.matches ? "instant" : "smooth",
});
}

function move(direction) {
// Equal-width grid columns snap one card at a time; native scrolling clamps the ends.
const step = cards[1].offsetLeft;
const index =
direction > 0
? Math.floor((track.scrollLeft + 2) / step) + 1
: Math.ceil((track.scrollLeft - 2) / step) - 1;
goTo(index * step);
}

previous.addEventListener("click", () => move(-1), { signal });
next.addEventListener("click", () => move(1), { signal });
track.addEventListener("scroll", update, { passive: true, signal });
const keys = {
ArrowLeft: () => move(-1),
ArrowRight: () => move(1),
Home: () => goTo(0),
End: () => goTo(track.scrollWidth),
};
track.addEventListener(
"keydown",
(event) => {
if (event.target !== track || !keys[event.key]) return;
event.preventDefault();
keys[event.key]();
},
{ signal },
);
const observer = new ResizeObserver(update);
observer.observe(track);
section.querySelector(".comparison-controls").hidden = false;
update();
return () => observer.disconnect();
}
4 changes: 4 additions & 0 deletions examples/official-site/assets/landing/js/sections.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
/** Wire section controls; motion and live-preview loading live in separate modules. */

import { initComparison } from "./comparison.js";
import { initComponentDemos } from "./component-demos.js";
import { initTabs } from "./tabs.js";

export function initSections(root) {
const events = new AbortController();
const { signal } = events;
const cleanupDemos = initComponentDemos(root, signal);
const cleanupComparison = initComparison(root, signal);
initTabs(
root.querySelector('[aria-label="Application stack"]'),
"aria-pressed",
Expand Down Expand Up @@ -36,5 +39,6 @@ export function initSections(root) {
return () => {
events.abort();
cleanupDemos();
cleanupComparison();
};
}
Loading
Loading