mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-13 20:18:12 -07:00
website: redesign part 10
This commit is contained in:
@@ -3,14 +3,15 @@ main.learn {
|
||||
counter-reset: content-theme;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding-top: var(--top-offset);
|
||||
padding-bottom: calc(var(--top-offset) / 2);
|
||||
padding-block: var(--offset);
|
||||
max-height: 100dvh;
|
||||
overflow: auto;
|
||||
scrollbar-width: thin;
|
||||
font-size: var(--font-size-xs);
|
||||
line-height: var(--line-height-xs);
|
||||
text-transform: uppercase;
|
||||
padding-left: 0.5rem;
|
||||
margin-left: -0.5rem;
|
||||
|
||||
ol {
|
||||
list-style: none;
|
||||
@@ -38,16 +39,37 @@ main.learn {
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
scroll-margin-block: var(--offset);
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
margin-right: 1rem;
|
||||
|
||||
&::before {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:is(:hover, [aria-current="location"]) {
|
||||
&:is(:hover, :active) {
|
||||
margin-block: -0.25rem;
|
||||
margin-left: -0.5rem;
|
||||
padding: 0.25rem;
|
||||
padding-left: 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--white);
|
||||
background-color: var(--dark-gray);
|
||||
}
|
||||
|
||||
&[aria-current="location"] {
|
||||
color: var(--orange);
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: var(--black);
|
||||
background-color: var(--orange);
|
||||
}
|
||||
}
|
||||
|
||||
> ol > li > a::before {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
/** @param {HTMLElement} main */
|
||||
export function initScrollSpy(main) {
|
||||
const headings = [...main.querySelectorAll("article h1, article h2")];
|
||||
const visibleHeadings = new Set();
|
||||
const sections = [...main.querySelectorAll("section[id]")];
|
||||
const sectionStates = sections.map((section) => ({
|
||||
section,
|
||||
children: [...section.querySelectorAll(":scope > section")],
|
||||
intersecting: false,
|
||||
}));
|
||||
const stateBySection = new Map(
|
||||
sectionStates.map((state) => [state.section, state]),
|
||||
);
|
||||
const links = new Map(
|
||||
[...main.querySelectorAll('nav a[href^="#"]')].map((link) => [
|
||||
link.getAttribute("href"),
|
||||
@@ -12,12 +19,24 @@ export function initScrollSpy(main) {
|
||||
/** @type {string | null} */
|
||||
let current = null;
|
||||
|
||||
/** @param {Element} heading */
|
||||
function getHash(heading) {
|
||||
const section = /** @type {HTMLElement} */ (
|
||||
heading.closest("section[id]")
|
||||
/** @param {Element} section */
|
||||
function getVisibleHeight(section) {
|
||||
const rect = section.getBoundingClientRect();
|
||||
return Math.max(
|
||||
0,
|
||||
Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0),
|
||||
);
|
||||
return `#${section.id}`;
|
||||
}
|
||||
|
||||
/** @param {{ section: Element, children: Element[] }} state */
|
||||
function getOwnVisibleHeight(state) {
|
||||
let height = getVisibleHeight(state.section);
|
||||
|
||||
for (const child of state.children) {
|
||||
height -= getVisibleHeight(child);
|
||||
}
|
||||
|
||||
return Math.max(0, height);
|
||||
}
|
||||
|
||||
/** @param {string} hash */
|
||||
@@ -26,38 +45,62 @@ export function initScrollSpy(main) {
|
||||
}
|
||||
|
||||
/** @param {string} hash */
|
||||
function setCurrent(hash) {
|
||||
function setCurrentHash(hash) {
|
||||
if (hash === current) return;
|
||||
|
||||
if (current) getLink(current).removeAttribute("aria-current");
|
||||
getLink(hash).setAttribute("aria-current", "location");
|
||||
|
||||
const link = getLink(hash);
|
||||
link.setAttribute("aria-current", "location");
|
||||
link.scrollIntoView({ block: "nearest", inline: "nearest" });
|
||||
|
||||
history.replaceState(null, "", hash);
|
||||
current = hash;
|
||||
}
|
||||
|
||||
function getCurrentSection() {
|
||||
/** @type {{ section: Element, children: Element[] } | undefined} */
|
||||
let currentState;
|
||||
let currentHeight = 0;
|
||||
|
||||
for (const state of sectionStates) {
|
||||
if (!state.intersecting) continue;
|
||||
|
||||
const height = getOwnVisibleHeight(state);
|
||||
|
||||
if (height > currentHeight) {
|
||||
currentState = state;
|
||||
currentHeight = height;
|
||||
}
|
||||
}
|
||||
|
||||
return currentState?.section;
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (main.hidden) return;
|
||||
|
||||
const heading = headings.findLast((heading) =>
|
||||
visibleHeadings.has(heading),
|
||||
);
|
||||
if (heading) setCurrent(getHash(heading));
|
||||
const section = getCurrentSection();
|
||||
if (section) setCurrentHash(`#${section.id}`);
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isIntersecting) {
|
||||
visibleHeadings.add(entry.target);
|
||||
} else {
|
||||
visibleHeadings.delete(entry.target);
|
||||
}
|
||||
const state = /** @type {{ intersecting: boolean }} */ (
|
||||
stateBySection.get(entry.target)
|
||||
);
|
||||
state.intersecting = entry.isIntersecting;
|
||||
}
|
||||
|
||||
update();
|
||||
},
|
||||
{ rootMargin: "0px 0px -80% 0px" },
|
||||
{
|
||||
threshold: [
|
||||
0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1,
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
for (const heading of headings) observer.observe(heading);
|
||||
for (const section of sections) observer.observe(section);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
main.learn {
|
||||
--top-offset: 6rem;
|
||||
--offset: 6rem;
|
||||
--content-width: 52rem;
|
||||
|
||||
display: grid;
|
||||
@@ -9,8 +9,7 @@ main.learn {
|
||||
|
||||
article {
|
||||
counter-reset: theme;
|
||||
padding-top: var(--top-offset);
|
||||
padding-bottom: calc(var(--top-offset) / 2);
|
||||
padding-block: var(--offset);
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
@@ -18,10 +17,10 @@ main.learn {
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
display: block;
|
||||
height: var(--top-offset);
|
||||
margin-top: calc(-1 * var(--top-offset));
|
||||
height: var(--offset);
|
||||
margin-top: calc(-1 * var(--offset));
|
||||
margin-inline: auto;
|
||||
margin-bottom: calc(-1 * var(--top-offset));
|
||||
margin-bottom: calc(-1 * var(--offset));
|
||||
background: var(--black);
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -31,12 +30,12 @@ main.learn {
|
||||
counter-reset: topic;
|
||||
width: min(100%, var(--content-width));
|
||||
margin-inline: auto;
|
||||
scroll-margin-top: var(--top-offset);
|
||||
scroll-margin-top: var(--offset);
|
||||
}
|
||||
|
||||
> section:first-of-type {
|
||||
margin-top: calc(-1 * var(--top-offset));
|
||||
padding-top: var(--top-offset);
|
||||
margin-top: calc(-1 * var(--offset));
|
||||
padding-top: var(--offset);
|
||||
}
|
||||
|
||||
> section + section {
|
||||
@@ -45,14 +44,14 @@ main.learn {
|
||||
|
||||
section section {
|
||||
counter-increment: topic;
|
||||
scroll-margin-top: var(--top-offset);
|
||||
scroll-margin-top: var(--offset);
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
position: sticky;
|
||||
top: var(--top-offset);
|
||||
top: var(--offset);
|
||||
padding-bottom: 0.5rem;
|
||||
background: var(--black);
|
||||
line-height: 1;
|
||||
|
||||
Reference in New Issue
Block a user