mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-10 15:03:32 -07:00
website: redesign part 8
This commit is contained in:
@@ -14,15 +14,17 @@ npx --package typescript tsc --noEmit --pretty false | grep -v "modules/"
|
||||
|
||||
ALWAYS
|
||||
|
||||
- fast
|
||||
- very fast
|
||||
- light (memory)
|
||||
- KISS
|
||||
- DRY
|
||||
- very well organized
|
||||
- contained
|
||||
- colocated
|
||||
- composed
|
||||
- prefer one concept per file
|
||||
- prefer more files and folders than big files
|
||||
- reads like english
|
||||
- very easy to understand
|
||||
- very easy to maintain
|
||||
- easy to understand
|
||||
- maintainability
|
||||
- avoid defensive checks when the code itself guarantees correctness
|
||||
|
||||
@@ -1,24 +1,32 @@
|
||||
import { createCube } from "../cube/index.js";
|
||||
import { primaryRoutes } from "../routes.js";
|
||||
|
||||
const header = document.createElement("header");
|
||||
export function createHeader() {
|
||||
const header = document.createElement("header");
|
||||
|
||||
const home = document.createElement("a");
|
||||
const cube = document.createElement("span");
|
||||
const home = document.createElement("a");
|
||||
const cube = document.createElement("span");
|
||||
|
||||
home.href = "/";
|
||||
home.ariaLabel = "bitview home";
|
||||
cube.append(createCube());
|
||||
home.append(cube, "bitview");
|
||||
home.href = "/";
|
||||
home.ariaLabel = "bitview home";
|
||||
cube.append(createCube());
|
||||
home.append(cube, "bitview");
|
||||
|
||||
const nav = document.createElement("nav");
|
||||
nav.setAttribute("aria-label", "Primary");
|
||||
nav.innerHTML = `
|
||||
<ul>
|
||||
<li><a href="/explore" aria-current="page">Explore</a></li>
|
||||
<li><a href="/learn">Learn</a></li>
|
||||
<li><a href="/build">Build</a></li>
|
||||
</ul>
|
||||
`;
|
||||
const nav = document.createElement("nav");
|
||||
const list = document.createElement("ul");
|
||||
nav.setAttribute("aria-label", "Primary");
|
||||
|
||||
header.append(home, nav);
|
||||
document.body.append(header);
|
||||
for (const { pathname, label } of primaryRoutes) {
|
||||
const item = document.createElement("li");
|
||||
const anchor = document.createElement("a");
|
||||
|
||||
anchor.href = pathname;
|
||||
anchor.append(label);
|
||||
item.append(anchor);
|
||||
list.append(item);
|
||||
}
|
||||
|
||||
nav.append(list);
|
||||
header.append(home, nav);
|
||||
return header;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
body {
|
||||
> header {
|
||||
position: fixed;
|
||||
inset: 1.5rem 2rem auto;
|
||||
inset: 1.5rem var(--page-x) auto;
|
||||
z-index: var(--layer-header);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
|
||||
@@ -89,9 +89,9 @@
|
||||
|
||||
<!-- IMPORTMAP -->
|
||||
<link rel="stylesheet" href="/styles/reset.css" />
|
||||
<link rel="stylesheet" href="/styles/main.css" />
|
||||
<link rel="stylesheet" href="/styles/fonts.css" />
|
||||
<link rel="stylesheet" href="/styles/variables.css" />
|
||||
<link rel="stylesheet" href="/styles/fonts.css" />
|
||||
<link rel="stylesheet" href="/styles/main.css" />
|
||||
|
||||
<link rel="stylesheet" href="/cube/style.css" />
|
||||
<link rel="stylesheet" href="/header/style.css" />
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
/**
|
||||
* @param {{ title: string, children: Section[] }} section
|
||||
*/
|
||||
function createSectionId(section) {
|
||||
return section.title.toLowerCase().replaceAll(" ", "-");
|
||||
}
|
||||
import { createId } from "../../utils/id.js";
|
||||
|
||||
/**
|
||||
* @param {{ title: string, children: Section[] }} section
|
||||
* @param {number[]} indexes
|
||||
*/
|
||||
function createContentsItem(section, indexes) {
|
||||
function createContentsItem(section) {
|
||||
const item = document.createElement("li");
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = `#${createSectionId(section)}`;
|
||||
anchor.href = `#${createId(section.title)}`;
|
||||
anchor.append(section.title);
|
||||
|
||||
if (section.children.length) {
|
||||
const list = document.createElement("ol");
|
||||
|
||||
for (const [index, child] of section.children.entries()) {
|
||||
list.append(createContentsItem(child, indexes.concat(index + 1)));
|
||||
for (const child of section.children) {
|
||||
list.append(createContentsItem(child));
|
||||
}
|
||||
item.append(list);
|
||||
}
|
||||
@@ -35,8 +29,8 @@ export function createContents(sections) {
|
||||
|
||||
nav.setAttribute("aria-label", "Learn contents");
|
||||
|
||||
for (const [index, section] of sections.entries()) {
|
||||
list.append(createContentsItem(section, [index + 1]));
|
||||
for (const section of sections) {
|
||||
list.append(createContentsItem(section));
|
||||
}
|
||||
|
||||
nav.append(list);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
main.learn {
|
||||
> nav {
|
||||
counter-reset: content-theme;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding-top: var(--top-offset);
|
||||
@@ -44,7 +45,7 @@ main.learn {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:is(:hover, [aria-current="location"]) {
|
||||
color: var(--orange);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-79
@@ -1,5 +1,7 @@
|
||||
import { createContents } from "./contents/index.js";
|
||||
import { sections } from "./data.js";
|
||||
import { initScrollSpy } from "./scroll-spy.js";
|
||||
import { createId } from "../utils/id.js";
|
||||
|
||||
/** @param {string} label */
|
||||
function createChart(label) {
|
||||
@@ -14,23 +16,16 @@ function createChart(label) {
|
||||
return figure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} title
|
||||
*/
|
||||
function createSectionId(title) {
|
||||
return title.toLowerCase().replaceAll(" ", "-");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Section} section
|
||||
* @param {number[]} indexes
|
||||
* @param {number} [level]
|
||||
*/
|
||||
function createSection(section, indexes) {
|
||||
function createSection(section, level = 1) {
|
||||
const element = document.createElement("section");
|
||||
const title = document.createElement(indexes.length === 1 ? "h1" : "h2");
|
||||
const title = document.createElement(level === 1 ? "h1" : "h2");
|
||||
const anchor = document.createElement("a");
|
||||
const description = document.createElement("p");
|
||||
const id = createSectionId(section.title);
|
||||
const id = createId(section.title);
|
||||
|
||||
element.id = id;
|
||||
anchor.href = `#${id}`;
|
||||
@@ -39,84 +34,20 @@ function createSection(section, indexes) {
|
||||
description.append(section.description);
|
||||
element.append(title, description, createChart(section.chart));
|
||||
|
||||
for (const [index, child] of section.children.entries()) {
|
||||
element.append(createSection(child, indexes.concat(index + 1)));
|
||||
for (const child of section.children) {
|
||||
element.append(createSection(child, level + 1));
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} main */
|
||||
function initScrollSpy(main) {
|
||||
const headings = [...main.querySelectorAll("article h1, article h2")];
|
||||
const visibleHeadings = new Set();
|
||||
const links = new Map(
|
||||
[...main.querySelectorAll('nav a[href^="#"]')].map((link) => [
|
||||
link.getAttribute("href"),
|
||||
link,
|
||||
]),
|
||||
);
|
||||
|
||||
/** @type {string | null} */
|
||||
let current = null;
|
||||
|
||||
/** @param {Element} heading */
|
||||
function getHash(heading) {
|
||||
const section = /** @type {HTMLElement} */ (
|
||||
heading.closest("section[id]")
|
||||
);
|
||||
return `#${section.id}`;
|
||||
}
|
||||
|
||||
/** @param {string} hash */
|
||||
function getLink(hash) {
|
||||
return /** @type {HTMLAnchorElement} */ (links.get(hash));
|
||||
}
|
||||
|
||||
/** @param {string} hash */
|
||||
function setCurrent(hash) {
|
||||
if (hash === current) return;
|
||||
|
||||
if (current) getLink(current).removeAttribute("aria-current");
|
||||
getLink(hash).setAttribute("aria-current", "location");
|
||||
history.replaceState(null, "", hash);
|
||||
current = hash;
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (main.hidden) return;
|
||||
|
||||
const heading = headings.findLast((heading) =>
|
||||
visibleHeadings.has(heading),
|
||||
);
|
||||
if (heading) setCurrent(getHash(heading));
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isIntersecting) {
|
||||
visibleHeadings.add(entry.target);
|
||||
} else {
|
||||
visibleHeadings.delete(entry.target);
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
},
|
||||
{ rootMargin: "0px 0px -80% 0px" },
|
||||
);
|
||||
|
||||
for (const heading of headings) observer.observe(heading);
|
||||
}
|
||||
|
||||
export function createLearnPage() {
|
||||
const main = document.createElement("main");
|
||||
main.className = "learn";
|
||||
const article = document.createElement("article");
|
||||
|
||||
for (const [index, section] of sections.entries()) {
|
||||
article.append(createSection(section, [index + 1]));
|
||||
for (const section of sections) {
|
||||
article.append(createSection(section));
|
||||
}
|
||||
|
||||
main.append(createContents(sections), article);
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/** @param {HTMLElement} main */
|
||||
export function initScrollSpy(main) {
|
||||
const headings = [...main.querySelectorAll("article h1, article h2")];
|
||||
const visibleHeadings = new Set();
|
||||
const links = new Map(
|
||||
[...main.querySelectorAll('nav a[href^="#"]')].map((link) => [
|
||||
link.getAttribute("href"),
|
||||
link,
|
||||
]),
|
||||
);
|
||||
|
||||
/** @type {string | null} */
|
||||
let current = null;
|
||||
|
||||
/** @param {Element} heading */
|
||||
function getHash(heading) {
|
||||
const section = /** @type {HTMLElement} */ (
|
||||
heading.closest("section[id]")
|
||||
);
|
||||
return `#${section.id}`;
|
||||
}
|
||||
|
||||
/** @param {string} hash */
|
||||
function getLink(hash) {
|
||||
return /** @type {HTMLAnchorElement} */ (links.get(hash));
|
||||
}
|
||||
|
||||
/** @param {string} hash */
|
||||
function setCurrent(hash) {
|
||||
if (hash === current) return;
|
||||
|
||||
if (current) getLink(current).removeAttribute("aria-current");
|
||||
getLink(hash).setAttribute("aria-current", "location");
|
||||
history.replaceState(null, "", hash);
|
||||
current = hash;
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (main.hidden) return;
|
||||
|
||||
const heading = headings.findLast((heading) =>
|
||||
visibleHeadings.has(heading),
|
||||
);
|
||||
if (heading) setCurrent(getHash(heading));
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isIntersecting) {
|
||||
visibleHeadings.add(entry.target);
|
||||
} else {
|
||||
visibleHeadings.delete(entry.target);
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
},
|
||||
{ rootMargin: "0px 0px -80% 0px" },
|
||||
);
|
||||
|
||||
for (const heading of headings) observer.observe(heading);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
main.learn {
|
||||
--top-offset: 6rem;
|
||||
--sidebar-bottom: 1rem;
|
||||
--content-width: 52rem;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 14rem minmax(0, 1fr);
|
||||
gap: 4rem;
|
||||
padding: 0 2rem;
|
||||
padding: 0 var(--page-x);
|
||||
|
||||
article {
|
||||
counter-reset: theme;
|
||||
@@ -18,7 +18,6 @@ main.learn {
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
display: block;
|
||||
width: min(100%, 52rem);
|
||||
height: var(--top-offset);
|
||||
margin-top: calc(-1 * var(--top-offset));
|
||||
margin-inline: auto;
|
||||
@@ -30,7 +29,7 @@ main.learn {
|
||||
> section {
|
||||
counter-increment: theme;
|
||||
counter-reset: topic;
|
||||
width: min(100%, 52rem);
|
||||
width: min(100%, var(--content-width));
|
||||
margin-inline: auto;
|
||||
scroll-margin-top: var(--top-offset);
|
||||
}
|
||||
@@ -53,13 +52,15 @@ main.learn {
|
||||
h1,
|
||||
h2 {
|
||||
position: sticky;
|
||||
top: var(--top-offset);
|
||||
padding-bottom: 0.5rem;
|
||||
background: var(--black);
|
||||
line-height: 1;
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
color: white;
|
||||
color: var(--white);
|
||||
text-decoration: none;
|
||||
|
||||
&::before {
|
||||
@@ -90,8 +91,6 @@ main.learn {
|
||||
|
||||
h1 {
|
||||
z-index: 3;
|
||||
top: var(--top-offset);
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--dark-gray);
|
||||
font-size: 2.75rem;
|
||||
|
||||
@@ -102,9 +101,7 @@ main.learn {
|
||||
|
||||
h2 {
|
||||
z-index: 1;
|
||||
top: var(--top-offset);
|
||||
padding-top: 4.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px dashed var(--dark-gray);
|
||||
font-size: 1.5rem;
|
||||
|
||||
|
||||
+13
-48
@@ -1,17 +1,7 @@
|
||||
import "./header/index.js";
|
||||
import { createBuildPage } from "./build/index.js";
|
||||
import { createExplorePage } from "./explore/index.js";
|
||||
import { createHomePage } from "./home/index.js";
|
||||
import { createLearnPage } from "./learn/index.js";
|
||||
import { readCssDuration, wait } from "./utils/timing.js";
|
||||
|
||||
/** @type {Record<string, () => HTMLElement>} */
|
||||
const routes = {
|
||||
"/": createHomePage,
|
||||
"/build": createBuildPage,
|
||||
"/explore": createExplorePage,
|
||||
"/learn": createLearnPage,
|
||||
};
|
||||
import { createHeader } from "./header/index.js";
|
||||
import { createRoutePage, isRoute, normalizePath } from "./routes.js";
|
||||
import { getEventAnchor } from "./utils/event.js";
|
||||
import { revealPage, transitionPage } from "./utils/transition.js";
|
||||
|
||||
/** @type {HTMLElement | undefined} */
|
||||
let currentPage;
|
||||
@@ -19,24 +9,16 @@ let currentPage;
|
||||
/** @type {Map<string, HTMLElement>} */
|
||||
const pageByPath = new Map();
|
||||
|
||||
function waitForTransition() {
|
||||
return wait(readCssDuration("--transition-duration"));
|
||||
}
|
||||
const header = createHeader();
|
||||
document.body.append(header);
|
||||
|
||||
function waitForReveal() {
|
||||
return wait(readCssDuration("--reveal-duration"));
|
||||
}
|
||||
|
||||
/** @param {string} pathname */
|
||||
function normalizePath(pathname) {
|
||||
return pathname in routes ? pathname : "/";
|
||||
}
|
||||
const navLinks = [...header.querySelectorAll("nav a")];
|
||||
|
||||
/** @param {string} pathname */
|
||||
function updateCurrentLink(pathname) {
|
||||
const currentPath = normalizePath(pathname);
|
||||
|
||||
for (const link of document.querySelectorAll("body > header > nav a")) {
|
||||
for (const link of navLinks) {
|
||||
const linkPath = new URL(/** @type {HTMLAnchorElement} */ (link).href)
|
||||
.pathname;
|
||||
|
||||
@@ -53,7 +35,7 @@ function getPage(pathname) {
|
||||
let page = pageByPath.get(pathname);
|
||||
|
||||
if (!page) {
|
||||
page = routes[pathname]();
|
||||
page = createRoutePage(pathname);
|
||||
page.hidden = true;
|
||||
page.inert = true;
|
||||
pageByPath.set(pathname, page);
|
||||
@@ -85,16 +67,7 @@ function renderPage() {
|
||||
function navigate(pathname) {
|
||||
if (pathname === window.location.pathname) return;
|
||||
history.pushState(null, "", pathname);
|
||||
transitionPage();
|
||||
}
|
||||
|
||||
async function transitionPage() {
|
||||
document.documentElement.dataset.transition = "";
|
||||
await waitForTransition();
|
||||
renderPage();
|
||||
requestAnimationFrame(() => {
|
||||
delete document.documentElement.dataset.transition;
|
||||
});
|
||||
void transitionPage(renderPage);
|
||||
}
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
@@ -102,16 +75,14 @@ document.addEventListener("click", (event) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const anchor = /** @type {HTMLAnchorElement | null} */ (
|
||||
/** @type {HTMLElement} */ (event.target).closest("a[href]")
|
||||
);
|
||||
const anchor = getEventAnchor(event);
|
||||
if (!anchor) return;
|
||||
|
||||
const url = new URL(anchor.href);
|
||||
if (url.origin !== window.location.origin) return;
|
||||
if (url.pathname === window.location.pathname && url.hash) return;
|
||||
|
||||
if (!(url.pathname in routes)) return;
|
||||
if (!isRoute(url.pathname)) return;
|
||||
|
||||
event.preventDefault();
|
||||
navigate(url.pathname);
|
||||
@@ -122,11 +93,5 @@ window.addEventListener("popstate", renderPage);
|
||||
renderPage();
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
waitForTransition().then(() => {
|
||||
delete document.documentElement.dataset.loading;
|
||||
document.documentElement.dataset.revealing = "";
|
||||
waitForReveal().then(() => {
|
||||
delete document.documentElement.dataset.revealing;
|
||||
});
|
||||
});
|
||||
void revealPage();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { createBuildPage } from "./build/index.js";
|
||||
import { createExplorePage } from "./explore/index.js";
|
||||
import { createHomePage } from "./home/index.js";
|
||||
import { createLearnPage } from "./learn/index.js";
|
||||
|
||||
const pages = [
|
||||
{ pathname: "/", createPage: createHomePage },
|
||||
{ pathname: "/explore", label: "Explore", createPage: createExplorePage },
|
||||
{ pathname: "/learn", label: "Learn", createPage: createLearnPage },
|
||||
{ pathname: "/build", label: "Build", createPage: createBuildPage },
|
||||
];
|
||||
|
||||
/** @type {Record<string, () => HTMLElement>} */
|
||||
const routes = Object.fromEntries(
|
||||
pages.map(({ pathname, createPage }) => [pathname, createPage]),
|
||||
);
|
||||
|
||||
export const primaryRoutes = pages.flatMap(({ pathname, label }) =>
|
||||
label ? [{ pathname, label }] : [],
|
||||
);
|
||||
|
||||
/** @param {string} pathname */
|
||||
export function isRoute(pathname) {
|
||||
return pathname in routes;
|
||||
}
|
||||
|
||||
/** @param {string} pathname */
|
||||
export function normalizePath(pathname) {
|
||||
return isRoute(pathname) ? pathname : "/";
|
||||
}
|
||||
|
||||
/** @param {string} pathname */
|
||||
export function createRoutePage(pathname) {
|
||||
return routes[pathname]();
|
||||
}
|
||||
@@ -13,20 +13,13 @@ html {
|
||||
|
||||
body {
|
||||
> main {
|
||||
color: white;
|
||||
min-height: 100dvh;
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
min-height: 100dvh;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
|
||||
body::before {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
color-scheme: dark;
|
||||
|
||||
--white: oklch(95% 0 0);
|
||||
--dark-white: oklch(92.5% 0 0);
|
||||
@@ -44,11 +44,10 @@
|
||||
--font-size-xl: 1.25rem;
|
||||
--line-height-xl: calc(1.75 / 1.25);
|
||||
|
||||
--page-x: 2rem;
|
||||
--main-padding: 2rem;
|
||||
--negative-main-padding: calc(-1 * var(--main-padding));
|
||||
--font-weight-base: 400;
|
||||
--max-main-width: 70dvw;
|
||||
--layer-transition: 8;
|
||||
--layer-header: 10;
|
||||
--layer-loading: 100;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @param {Event} event
|
||||
* @param {string} selector
|
||||
*/
|
||||
export function getEventTarget(event, selector) {
|
||||
return /** @type {HTMLElement | null} */ (
|
||||
/** @type {HTMLElement} */ (event.target).closest(selector)
|
||||
);
|
||||
}
|
||||
|
||||
/** @param {Event} event */
|
||||
export function getEventAnchor(event) {
|
||||
return /** @type {HTMLAnchorElement | null} */ (
|
||||
getEventTarget(event, "a[href]")
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/** @param {string} value */
|
||||
export function createId(value) {
|
||||
return value.toLowerCase().replaceAll(" ", "-");
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/** @param {number} ms */
|
||||
export function wait(ms) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {string} name */
|
||||
export function readCssDuration(name) {
|
||||
const value = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue(name)
|
||||
.trim();
|
||||
|
||||
return Number.parseFloat(value) * (value.endsWith("ms") ? 1 : 1000);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
let transitionId = 0;
|
||||
|
||||
/** @param {number} ms */
|
||||
function wait(ms) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {string} name */
|
||||
function readCssDuration(name) {
|
||||
const value = getComputedStyle(document.documentElement)
|
||||
.getPropertyValue(name)
|
||||
.trim();
|
||||
|
||||
return Number.parseFloat(value) * (value.endsWith("ms") ? 1 : 1000);
|
||||
}
|
||||
|
||||
function waitForTransition() {
|
||||
return wait(readCssDuration("--transition-duration"));
|
||||
}
|
||||
|
||||
function waitForReveal() {
|
||||
return wait(readCssDuration("--reveal-duration"));
|
||||
}
|
||||
|
||||
/** @param {() => void} render */
|
||||
export async function transitionPage(render) {
|
||||
const id = ++transitionId;
|
||||
document.documentElement.dataset.transition = "";
|
||||
await waitForTransition();
|
||||
if (id !== transitionId) return;
|
||||
|
||||
render();
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (id === transitionId) delete document.documentElement.dataset.transition;
|
||||
});
|
||||
}
|
||||
|
||||
export async function revealPage() {
|
||||
await waitForTransition();
|
||||
delete document.documentElement.dataset.loading;
|
||||
document.documentElement.dataset.revealing = "";
|
||||
await waitForReveal();
|
||||
delete document.documentElement.dataset.revealing;
|
||||
}
|
||||
Reference in New Issue
Block a user