mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-24 01:18:10 -07:00
website: redesign part 3
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
export const sections = [
|
||||
{
|
||||
title: "Supply",
|
||||
description:
|
||||
"How bitcoin moves from issuance into long-term ownership, profit, loss, and distribution.",
|
||||
chart: "Circulating supply",
|
||||
children: [
|
||||
{
|
||||
title: "Profitability",
|
||||
description:
|
||||
"Which coins sit in profit or loss, and how that balance changes through cycles.",
|
||||
chart: "Supply in profit",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
title: "Age",
|
||||
description:
|
||||
"How long coins have remained still, from fresh movement to deep dormancy.",
|
||||
chart: "Supply by age",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
title: "Distribution",
|
||||
description:
|
||||
"How supply is spread across addresses, scripts, cohorts, and balance ranges.",
|
||||
chart: "Supply distribution",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Capitalization",
|
||||
description:
|
||||
"Different ways to value the network by market price, realized cost, and accumulated flows.",
|
||||
chart: "Capitalization overview",
|
||||
children: [
|
||||
{
|
||||
title: "Market Cap",
|
||||
description:
|
||||
"The current market value of circulating bitcoin at spot price.",
|
||||
chart: "Market capitalization",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
title: "Realized Cap",
|
||||
description:
|
||||
"The aggregate value of coins priced where they last moved on-chain.",
|
||||
chart: "Realized capitalization",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
title: "Value Bands",
|
||||
description:
|
||||
"How market value compares with cost basis and historical valuation ranges.",
|
||||
chart: "Valuation bands",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Activity",
|
||||
description:
|
||||
"How often the chain is used, how value moves, and how demand appears in fees and transactions.",
|
||||
chart: "Network activity",
|
||||
children: [
|
||||
{
|
||||
title: "Transactions",
|
||||
description:
|
||||
"Confirmed transaction count, throughput, and block-level settlement patterns.",
|
||||
chart: "Transaction count",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
title: "Fees",
|
||||
description:
|
||||
"The cost users pay for block space and what that reveals about demand.",
|
||||
chart: "Fee rate",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
title: "Addresses",
|
||||
description:
|
||||
"Address creation, reuse, activity, and balance changes across the network.",
|
||||
chart: "Active addresses",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Mining",
|
||||
description:
|
||||
"The security budget, difficulty adjustments, pool behavior, and miner revenue.",
|
||||
chart: "Mining overview",
|
||||
children: [
|
||||
{
|
||||
title: "Hashrate",
|
||||
description:
|
||||
"Estimated computational power securing the network over time.",
|
||||
chart: "Hashrate",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
title: "Difficulty",
|
||||
description:
|
||||
"How Bitcoin adjusts mining difficulty to keep block production steady.",
|
||||
chart: "Difficulty",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
title: "Rewards",
|
||||
description:
|
||||
"Subsidy, fees, and the changing economics of block production.",
|
||||
chart: "Miner rewards",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Market",
|
||||
description:
|
||||
"Price behavior, returns, volatility, and the market context around on-chain patterns.",
|
||||
chart: "Market overview",
|
||||
children: [
|
||||
{
|
||||
title: "Price",
|
||||
description:
|
||||
"Bitcoin price across time, cycles, drawdowns, and all-time highs.",
|
||||
chart: "Price",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
title: "Returns",
|
||||
description:
|
||||
"How returns vary by holding period, entry point, and cycle phase.",
|
||||
chart: "Returns",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
title: "Volatility",
|
||||
description:
|
||||
"The scale and rhythm of price movement across different windows.",
|
||||
chart: "Volatility",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,147 @@
|
||||
import { sections } from "./data.js";
|
||||
|
||||
/** @param {string} label */
|
||||
function createChart(label) {
|
||||
const figure = document.createElement("figure");
|
||||
const chart = document.createElement("div");
|
||||
const caption = document.createElement("figcaption");
|
||||
|
||||
chart.append(label);
|
||||
caption.append(label);
|
||||
figure.append(chart, caption);
|
||||
|
||||
return figure;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} title
|
||||
* @param {number[]} indexes
|
||||
*/
|
||||
function createSectionId(title, indexes) {
|
||||
return `${indexes.join("-")}-${title.toLowerCase().replaceAll(" ", "-")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Section} section
|
||||
* @param {number[]} indexes
|
||||
*/
|
||||
function createSection(section, indexes) {
|
||||
const element = document.createElement("section");
|
||||
const title = document.createElement(indexes.length === 1 ? "h1" : "h2");
|
||||
const anchor = document.createElement("a");
|
||||
const description = document.createElement("p");
|
||||
|
||||
element.id = createSectionId(section.title, indexes);
|
||||
anchor.href = `#${element.id}`;
|
||||
anchor.append(section.title);
|
||||
title.append(anchor);
|
||||
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)));
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ title: string, children: Section[] }} section
|
||||
* @param {number[]} indexes
|
||||
*/
|
||||
function createContentsItem(section, indexes) {
|
||||
const item = document.createElement("li");
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = `#${createSectionId(section.title, indexes)}`;
|
||||
anchor.append(section.title);
|
||||
item.append(anchor);
|
||||
|
||||
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)));
|
||||
}
|
||||
item.append(list);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
function createContents() {
|
||||
const nav = document.createElement("nav");
|
||||
const list = document.createElement("ol");
|
||||
|
||||
nav.setAttribute("aria-label", "Learn contents");
|
||||
|
||||
for (const [index, section] of sections.entries()) {
|
||||
list.append(createContentsItem(section, [index + 1]));
|
||||
}
|
||||
|
||||
nav.append(list);
|
||||
return nav;
|
||||
}
|
||||
|
||||
/** @param {HTMLElement} main */
|
||||
function initScrollSpy(main) {
|
||||
const sections = [...main.querySelectorAll("section[id]")];
|
||||
const visible = new Set();
|
||||
const links = new Map(
|
||||
[...main.querySelectorAll('nav a[href^="#"]')].map((link) => [
|
||||
link.getAttribute("href"),
|
||||
link,
|
||||
]),
|
||||
);
|
||||
|
||||
/** @type {string | null} */
|
||||
let current = null;
|
||||
|
||||
function update() {
|
||||
const section = sections.find((section) => visible.has(section.id));
|
||||
if (!section) return;
|
||||
|
||||
const hash = `#${section.id}`;
|
||||
if (hash === current) return;
|
||||
|
||||
links.get(current)?.removeAttribute("aria-current");
|
||||
links.get(hash)?.setAttribute("aria-current", "location");
|
||||
history.replaceState(null, "", `/learn${hash}`);
|
||||
current = hash;
|
||||
}
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.isIntersecting) {
|
||||
visible.add(entry.target.id);
|
||||
} else {
|
||||
visible.delete(entry.target.id);
|
||||
}
|
||||
}
|
||||
update();
|
||||
});
|
||||
|
||||
for (const section of sections) {
|
||||
observer.observe(section);
|
||||
}
|
||||
}
|
||||
|
||||
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]));
|
||||
}
|
||||
|
||||
main.append(article, createContents());
|
||||
initScrollSpy(main);
|
||||
return main;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} Section
|
||||
* @property {string} title
|
||||
* @property {string} description
|
||||
* @property {string} chart
|
||||
* @property {Section[]} children
|
||||
*/
|
||||
@@ -0,0 +1,129 @@
|
||||
main.learn {
|
||||
min-height: 100dvh;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 14rem;
|
||||
gap: 4rem;
|
||||
padding: 8rem 2rem 6rem;
|
||||
color: white;
|
||||
|
||||
article {
|
||||
width: min(100%, 52rem);
|
||||
justify-self: center;
|
||||
|
||||
section {
|
||||
scroll-margin-top: 6rem;
|
||||
}
|
||||
|
||||
> section + section {
|
||||
margin-top: 8rem;
|
||||
}
|
||||
|
||||
section section {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
|
||||
&::before {
|
||||
content: "#";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 100%;
|
||||
translate: 0 -50%;
|
||||
opacity: 0;
|
||||
user-select: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 0.125em;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: var(--orange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.75rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 1rem;
|
||||
color: var(--dark-white);
|
||||
}
|
||||
|
||||
figure {
|
||||
margin-top: 2rem;
|
||||
color: var(--gray);
|
||||
font-size: var(--font-size-xs);
|
||||
text-transform: uppercase;
|
||||
|
||||
> div {
|
||||
height: 18rem;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 1px solid var(--dark-gray);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
figcaption {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
> nav {
|
||||
position: sticky;
|
||||
top: 6rem;
|
||||
align-self: start;
|
||||
font-size: var(--font-size-xs);
|
||||
line-height: var(--line-height-sm);
|
||||
text-transform: uppercase;
|
||||
|
||||
ol {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ol ol {
|
||||
margin-top: 0.5rem;
|
||||
margin-left: 1rem;
|
||||
color: var(--gray);
|
||||
}
|
||||
|
||||
li + li {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: var(--orange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user