From 1b5d05b794681dbd01b3e25664db32e10e6456ee Mon Sep 17 00:00:00 2001 From: Labyricorn Date: Sat, 22 Aug 2026 11:01:14 -0700 Subject: [PATCH] Add circuit signal homepage hero --- assets/static/hero-circuit.js | 48 +++++++ assets/static/style.css | 234 ++++++++++++++++++++++++++++++++++ content/contents.lr | 8 +- templates/base.html | 3 +- templates/home.html | 81 +++++++++++- 5 files changed, 364 insertions(+), 10 deletions(-) create mode 100644 assets/static/hero-circuit.js diff --git a/assets/static/hero-circuit.js b/assets/static/hero-circuit.js new file mode 100644 index 0000000..62059ea --- /dev/null +++ b/assets/static/hero-circuit.js @@ -0,0 +1,48 @@ +(() => { + const hero = document.querySelector("[data-circuit-hero]"); + if (!hero) return; + + const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; + const sequence = [ + ["projects", 180], ["articles", 320], ["blog", 460], + ["github", 820], ["gitea", 930], ["linkedin", 1040], + ["twitch", 1150], ["youtube", 1260], ["kofi", 1370], + ]; + let run = 0; + let timers = []; + + const clearTimers = () => { + timers.forEach(window.clearTimeout); + timers = []; + }; + + const revealAll = () => { + hero.querySelectorAll(".circuit-node").forEach((node) => node.classList.add("is-ready")); + hero.querySelectorAll(".circuit-live path").forEach((path) => path.classList.add("is-live")); + }; + + const start = () => { + run += 1; + const token = run; + clearTimers(); + hero.classList.remove("is-glitching"); + hero.querySelectorAll(".circuit-node").forEach((node) => node.classList.remove("is-ready")); + hero.querySelectorAll(".circuit-live path").forEach((path) => path.classList.remove("is-live")); + + if (reducedMotion) { + revealAll(); + return; + } + + sequence.forEach(([name, delay]) => { + timers.push(window.setTimeout(() => { + if (token !== run) return; + hero.querySelector(`[data-route-node="${name}"]`)?.classList.add("is-ready"); + hero.querySelector(`#route-${name}`)?.classList.add("is-live"); + }, delay)); + }); + }; + + hero.querySelector("[data-circuit-replay]")?.addEventListener("click", start); + start(); +})(); diff --git a/assets/static/style.css b/assets/static/style.css index 84d0d3f..d858b69 100644 --- a/assets/static/style.css +++ b/assets/static/style.css @@ -41,6 +41,215 @@ h1 { max-width: 880px; margin: 0; font-size: clamp(30px, 4vw, 45px); font-style: .dek { max-width: 700px; margin-top: 18px; color: var(--muted); } .dek p { margin: 0; } +.circuit-hero { + --circuit-gold: #c5a059; + --circuit-cyan: #65f7ff; + --circuit-pink: #ff4fd8; + position: relative; + width: min(100% - 40px, 1200px); + height: clamp(620px, 78vh, 760px); + min-height: 620px; + margin: 34px auto; + overflow: hidden; + border: 1px solid var(--rule); + background: + linear-gradient(180deg, rgba(255,255,255,.025), transparent 14%), + radial-gradient(circle at 28% 28%, rgba(101,247,255,.035), transparent 28%), + #080a0a; + isolation: isolate; +} +.circuit-hero::before { + content: ""; + position: absolute; + z-index: 1; + inset: 0; + pointer-events: none; + background-image: + linear-gradient(rgba(255,255,255,.018) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,.018) 1px, transparent 1px); + background-size: 36px 36px; + mask-image: linear-gradient(to bottom, #000, transparent 82%); +} +.circuit-hero::after { + content: ""; + position: absolute; + z-index: 20; + inset: 0; + pointer-events: none; + opacity: .1; + background: repeating-linear-gradient(0deg, transparent 0 3px, rgba(255,255,255,.025) 3px 4px); +} +.circuit-hero-topline { + position: absolute; + z-index: 14; + inset: 22px 28px auto; + display: flex; + justify-content: space-between; + color: var(--dim); + font: 500 9px var(--mono); + letter-spacing: .14em; + text-transform: uppercase; +} +.circuit-hero-topline strong { color: var(--circuit-gold); font-weight: 600; } +.circuit-brand-block { + position: absolute; + z-index: 10; + top: clamp(72px, 11vh, 104px); + left: clamp(28px, 5vw, 72px); + width: min(610px, 64vw); +} +.circuit-brand-kicker { + margin: 0 0 12px; + color: var(--circuit-gold); + font: 600 11px/1.2 var(--mono); + letter-spacing: .16em; + text-transform: uppercase; +} +.circuit-brand-block h1 { + max-width: none; + margin: 0; + font: 700 clamp(45px, 7vw, 98px)/.86 var(--serif); + font-style: normal; + letter-spacing: -.055em; + text-shadow: 0 0 24px rgba(197,160,89,.05); +} +.circuit-dek { + max-width: 430px; + margin-top: 22px; + color: var(--muted); + font-size: clamp(15px, 1.6vw, 17px); +} +.circuit-dek p { margin: 0; } +.circuit-status { + display: inline-flex; + align-items: center; + gap: 9px; + margin: 20px 0 0; + border: 1px solid rgba(197,160,89,.45); + padding: 6px 9px; + color: var(--circuit-gold); + background: rgba(18,18,18,.68); + font: 500 9px var(--mono); + letter-spacing: .08em; + text-transform: uppercase; +} +.circuit-status span { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--circuit-cyan); + box-shadow: 0 0 10px var(--circuit-cyan); + animation: circuit-pulse 1.6s ease-in-out infinite; +} +.circuit-stage { position: absolute; z-index: 2; inset: 0; } +.circuit-stage svg { display: block; width: 100%; height: 100%; } +.circuit-stage path { fill: none; vector-effect: non-scaling-stroke; } +.circuit-ambient path { stroke: #1d2221; stroke-width: 1; opacity: .72; } +.circuit-ambient path:nth-child(3n+1) { stroke: rgba(101,247,255,.22); } +.circuit-ambient path:nth-child(3n+2) { stroke: rgba(255,79,216,.16); } +.circuit-ghost path { stroke: #020303; stroke-width: 8; opacity: .9; } +.circuit-traces path, +.circuit-live path { stroke: #303837; stroke-width: 2; } +.circuit-live path { + opacity: .34; + stroke: var(--circuit-cyan); + stroke-dasharray: 22 52; + filter: drop-shadow(0 0 5px rgba(101,247,255,.36)); + transition: opacity .35s ease; +} +.circuit-live path.circuit-live-alt { + stroke: var(--circuit-pink); + filter: drop-shadow(0 0 5px rgba(255,79,216,.3)); +} +.js .circuit-live path { opacity: 0; } +.js .circuit-live path.is-live { + opacity: .82; + animation: circuit-packet 1.65s linear infinite; +} +.circuit-vias circle { + fill: #090b0b; + stroke: var(--circuit-cyan); + stroke-width: 1.5; + vector-effect: non-scaling-stroke; +} +.circuit-vias circle:nth-child(2n) { stroke: var(--circuit-pink); } +.circuit-node { + position: absolute; + z-index: 12; + display: inline-block; + color: var(--circuit-gold); + font-family: var(--mono); + font-weight: 600; + letter-spacing: .09em; + line-height: 1; + text-transform: uppercase; + transition: opacity .28s ease, transform .28s ease, color .2s ease, filter .2s ease; +} +.js .circuit-node { opacity: .22; transform: translateY(3px); pointer-events: none; } +.js .circuit-node.is-ready { opacity: 1; transform: none; pointer-events: auto; } +.circuit-node::before { + content: ""; + position: absolute; + top: 50%; + left: -14px; + width: 7px; + height: 7px; + border: 1px solid currentColor; + transform: translateY(-50%) rotate(45deg); + opacity: .65; +} +.circuit-node:hover, +.circuit-node:focus-visible { color: #b6fbff; filter: drop-shadow(0 0 8px rgba(101,247,255,.35)); outline: none; } +.circuit-node:hover::before, +.circuit-node:focus-visible::before { background: currentColor; box-shadow: 0 0 10px currentColor; } +.circuit-node-main { color: var(--ink); font-size: clamp(22px, 2.7vw, 38px); font-weight: 600; } +.circuit-node-main i { color: var(--circuit-gold); font-style: normal; animation: circuit-cursor .75s steps(1) infinite; } +.circuit-node-sub { font-size: clamp(11px, 1.15vw, 13px); } +.circuit-node[data-route-node="projects"] { left: 67%; top: 27%; } +.circuit-node[data-route-node="articles"] { left: 72%; top: 47%; } +.circuit-node[data-route-node="blog"] { left: 61%; top: 68%; } +.circuit-node[data-route-node="github"] { left: 27%; top: 73%; } +.circuit-node[data-route-node="gitea"] { left: 36%; top: 81%; } +.circuit-node[data-route-node="linkedin"] { left: 49%; top: 87%; } +.circuit-node[data-route-node="twitch"] { left: 63%; top: 91%; } +.circuit-node[data-route-node="youtube"] { left: 77%; top: 83%; } +.circuit-node[data-route-node="kofi"] { left: 88%; top: 72%; } +.circuit-legend { + position: absolute; + z-index: 12; + right: 24px; + bottom: 20px; + display: flex; + gap: 18px; + color: var(--dim); + font: 500 8px var(--mono); + letter-spacing: .1em; + text-transform: uppercase; +} +.circuit-legend span:first-child { color: var(--circuit-cyan); } +.circuit-replay { + display: none; + position: absolute; + z-index: 22; + bottom: 17px; + left: 24px; + border: 1px solid var(--rule); + background: rgba(8,10,10,.86); + padding: 7px 10px; + color: var(--muted); + cursor: pointer; + font: 500 8px var(--mono); + letter-spacing: .1em; + text-transform: uppercase; +} +.js .circuit-replay { display: block; } +.circuit-replay:hover, +.circuit-replay:focus-visible { border-color: var(--circuit-cyan); color: var(--circuit-cyan); } + +@keyframes circuit-packet { to { stroke-dashoffset: -74; } } +@keyframes circuit-cursor { 50% { opacity: 0; } } +@keyframes circuit-pulse { 50% { opacity: .35; transform: scale(.8); } } + .activity-section { min-height: 370px; padding: 48px 0 72px; } .homepage-discovery-grid { display: grid; grid-template-columns: minmax(0, 1.65fr) minmax(290px, 1fr); gap: 36px; align-items: start; } .homepage-panel { min-width: 0; } @@ -367,6 +576,25 @@ h1 { max-width: 880px; margin: 0; font-size: clamp(30px, 4vw, 45px); font-style: .primary-nav { margin-top: 14px; justify-content: space-between; gap: 14px; } .header-meta { display: none; } .hero, .hero.compact { padding-block: 44px; } + .circuit-hero { width: calc(100% - 20px); height: 760px; min-height: 760px; margin-block: 20px; } + .circuit-hero-topline { inset: 18px 16px auto; font-size: 8px; } + .circuit-hero-topline span:last-child { display: none; } + .circuit-brand-block { top: 72px; left: 24px; width: 82vw; } + .circuit-brand-block h1 { font-size: clamp(48px, 14vw, 62px); } + .circuit-dek { max-width: 85vw; font-size: 14px; } + .circuit-node-main { font-size: 21px; } + .circuit-node-sub { font-size: 10px; } + .circuit-node[data-route-node="projects"] { left: 32%; top: 42%; } + .circuit-node[data-route-node="articles"] { left: 38%; top: 53%; } + .circuit-node[data-route-node="blog"] { left: 28%; top: 64%; } + .circuit-node[data-route-node="github"] { left: 18%; top: 74%; } + .circuit-node[data-route-node="gitea"] { left: 56%; top: 74%; } + .circuit-node[data-route-node="linkedin"] { left: 18%; top: 81%; } + .circuit-node[data-route-node="twitch"] { left: 60%; top: 81%; } + .circuit-node[data-route-node="youtube"] { left: 18%; top: 88%; } + .circuit-node[data-route-node="kofi"] { left: 64%; top: 88%; } + .circuit-legend { display: none; } + .circuit-replay { left: 16px; bottom: 13px; } h1 { font-size: 30px; } .homepage-discovery-grid { grid-template-columns: 1fr; gap: 48px; } .featured-project-card { padding: 20px; } @@ -426,3 +654,9 @@ h1 { max-width: 880px; margin: 0; font-size: clamp(30px, 4vw, 45px); font-style: .footer-inner { align-items: flex-start; flex-direction: column; padding-block: 28px; } .site-footer nav { flex-wrap: wrap; } } + +@media (prefers-reduced-motion: reduce) { + .circuit-status span, + .circuit-node-main i, + .circuit-live path.is-live { animation: none; } +} diff --git a/content/contents.lr b/content/contents.lr index 9681acf..dcea550 100644 --- a/content/contents.lr +++ b/content/contents.lr @@ -2,10 +2,10 @@ _model: page --- title: Home --- -eyebrow: The Chronicle +eyebrow: Developer / Devlog / Project Hub --- -summary: Things built. Lessons learned. Work still in progress. +summary: Things built. Signals sent. --- body: -A running record of projects, experiments, technical writing, and the occasional -idea that refused to stay hypothetical. +Projects, experiments, technical writing, and ideas moving from signal to +finished work. diff --git a/templates/base.html b/templates/base.html index 9a15740..8cfcc4f 100644 --- a/templates/base.html +++ b/templates/base.html @@ -9,9 +9,10 @@ - + + diff --git a/templates/home.html b/templates/home.html index 0c944e3..c03191f 100644 --- a/templates/home.html +++ b/templates/home.html @@ -1,9 +1,80 @@ -
-
-

{{ this.eyebrow }}

-

{{ this.summary }}

-
{{ this.body }}
+
+
+ LABYRICORN // SIGNAL ROUTER + REG. NO. 004 — EST. LOCAL
+ +
+

{{ this.eyebrow }}

+

{{ this.summary }}

+
{{ this.body }}
+

network active / routes resolving

+
+ + + + + + +