This commit is contained in:
Priec
2026-06-01 23:40:17 +02:00
parent 99f255fc29
commit 462a53853f
12 changed files with 4403 additions and 238 deletions

View File

@@ -1,60 +1,46 @@
/* ==========================================================================
tui-pages website - animated ASCII art layer (chafa-rendered)
Loaded AFTER site.css. Provides:
- .tp-ascii-rain full-bleed body background, slow drifting pattern
sourced from static/img/og-image-bg.svg
All animations respect prefers-reduced-motion.
tui-pages website - animated ASCII mountain background (chafa-rendered)
The mountain background is JS-driven: tools/mountain.py renders 60
procedural 3D mountain-flyover PNG frames; chafa converts each to 80x24
ASCII; tools/build_mountain_js.py bundles them into static/js/mountain.js.
static/js/mountain-bg.js cycles the frames at 12 fps into a single
<pre class="tp-mountain-frame"> element mounted at the bottom of <body>.
This CSS file:
- positions the frame as a full-bleed fixed background
- tints the dense characters rust-orange with text-shadow + glow
- adds a CRT scanline overlay
- disables the animation under prefers-reduced-motion
========================================================================== */
/* Body-wide ASCII art background ---------------------------------------- */
/* Fixed full-bleed layer behind all content. Renders a chafa output of the
og-image-bg.svg (black background + "tui-pages" wordmark + headline +
subtitle), drawn as ASCII glyphs in a single full-width tile. Two copies
of the tile are stacked vertically and the whole track drifts slowly
upward, looping seamlessly when the top copy exits the viewport.
The hero is a static <img src="static/img/terminal-default.svg"> as
designed - this file does NOT touch the hero.
Regenerate the source tile with `make ascii` (uses chafa on
static/img/og-image-bg.svg). The chafa output is in static/ascii/rain.txt
and is 240 columns wide. */
.tp-ascii-rain {
/* Full-bleed ASCII mountain background ---------------------------------- */
.tp-mountain-frame {
position: fixed;
inset: 0;
z-index: 0; /* sit above the page background, below the content */
z-index: 0;
pointer-events: none;
opacity: 0.14;
overflow: hidden;
font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: clamp(6px, 0.9vw, 9px);
line-height: 1.05;
color: #f4a26b; /* warm rust-orange tint, picks up the brand */
white-space: pre;
text-shadow: 0 0 6px rgba(183, 65, 14, 0.25); /* faint phosphor glow */
will-change: transform;
}
/* The track holds two byte-identical copies of rain.txt stacked vertically.
translateY(0 -> -50%) is a perfect seamless loop. */
.tp-ascii-rain-track {
display: block;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
margin: 0;
padding: 0;
animation: tp-ascii-drift 90s linear infinite;
will-change: transform;
}
.tp-ascii-rain-cell {
display: block;
white-space: pre; /* preserve the chafa line breaks */
font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: clamp(8px, 1.2vw, 14px);
line-height: 1.0;
letter-spacing: 0;
color: #f4a26b; /* warm rust-orange tint, picks up the brand */
text-shadow: 0 0 8px rgba(183, 65, 14, 0.35); /* phosphor glow */
opacity: 0.45;
white-space: pre;
background: transparent;
overflow: hidden;
user-select: none;
}
/* CRT scanlines overlay - a thin moving line every 2px. Faint so it doesn't
fight the page content. */
.tp-ascii-rain::after {
.tp-mountain-frame::after {
content: "";
position: absolute;
inset: 0;
@@ -62,36 +48,28 @@
to bottom,
transparent 0,
transparent 2px,
rgba(0, 0, 0, 0.18) 2px,
rgba(0, 0, 0, 0.18) 3px
rgba(0, 0, 0, 0.20) 2px,
rgba(0, 0, 0, 0.20) 3px
);
pointer-events: none;
animation: tp-ascii-scanline 8s linear infinite;
animation: tp-mtn-scanline 8s linear infinite;
mix-blend-mode: multiply;
}
@keyframes tp-ascii-drift {
0% { transform: translateY(0); }
100% { transform: translateY(-50%); }
}
@keyframes tp-ascii-scanline {
@keyframes tp-mtn-scanline {
0% { background-position: 0 0; }
100% { background-position: 0 6px; }
}
/* The hero text is high-contrast on its own (zinc-50 / accent), and the
body ASCII rain is at 14% opacity. No overlay needed — the rain shows
through everywhere. */
/* Light theme: invert to dark text on light, dimmer, no glow. */
:root[data-theme="light"] .tp-ascii-rain {
opacity: 0.10;
color: #1f1f23;
/* Light theme: dim the background, drop the glow, switch to dark text. */
:root[data-theme="winter"] .tp-mountain-frame {
opacity: 0.20;
color: #2a1810;
text-shadow: none;
mix-blend-mode: multiply;
}
:root[data-theme="light"] .tp-ascii-rain::after {
:root[data-theme="winter"] .tp-mountain-frame::after {
background: repeating-linear-gradient(
to bottom,
transparent 0,
@@ -99,13 +77,12 @@
rgba(255, 255, 255, 0.12) 2px,
rgba(255, 255, 255, 0.12) 3px
);
mix-blend-mode: screen;
}
/* Reduced motion: pause the drift, keep the static background. */
/* Reduced motion: keep the first frame static, no scanline drift. */
@media (prefers-reduced-motion: reduce) {
.tp-ascii-rain-track,
.tp-ascii-rain::after {
.tp-mountain-frame::after {
animation: none !important;
transform: none !important;
}
}

View File

@@ -31,7 +31,7 @@ html {
--tp-hero-to: #18181b;
}
:root[data-theme="light"] {
:root[data-theme="winter"] {
--tp-accent: #93330c;
--tp-accent-fg: #fff5f0;
--tp-grid-line: rgba(228, 228, 231, 0.7);
@@ -55,7 +55,7 @@ body {
/* ---------- Hero --------------------------------------------------------- */
.tp-hero {
background: transparent; /* let the body ASCII rain show through */
background: transparent; /* let the mountain ASCII show through */
position: relative;
isolation: isolate;
}
@@ -119,7 +119,7 @@ body {
-webkit-backdrop-filter: saturate(160%) blur(12px);
border-bottom: 1px solid rgba(63, 63, 70, 0.4);
}
:root[data-theme="light"] .tp-nav {
:root[data-theme="winter"] .tp-nav {
background: rgba(255, 255, 255, 0.7);
border-bottom-color: rgba(228, 228, 231, 0.7);
}
@@ -138,11 +138,11 @@ body {
background: rgba(24, 24, 27, 0.75);
transform: translateY(-2px);
}
:root[data-theme="light"] .tp-card {
:root[data-theme="winter"] .tp-card {
background: rgba(255, 255, 255, 0.6);
border-color: rgba(228, 228, 231, 0.8);
}
:root[data-theme="light"] .tp-card:hover {
:root[data-theme="winter"] .tp-card:hover {
background: rgba(255, 255, 255, 0.9);
border-color: rgba(183, 65, 14, 0.4);
}
@@ -195,7 +195,7 @@ body {
color: #f4f4f5;
line-height: 1;
}
:root[data-theme="light"] .tp-kbd {
:root[data-theme="winter"] .tp-kbd {
background: #fff;
color: #18181b;
border-color: #d4d4d8;
@@ -254,7 +254,7 @@ body {
/* ---------- No-FOUC theme flash ----------------------------------------- */
html:not([data-theme]) body { background: #09090b; }
:root[data-theme="light"] body { background: #fafafa; }
:root[data-theme="winter"] body { background: #fafafa; }
/* ---------- Alpine x-cloak (prevents flash of un-initialised content) --- */
[x-cloak] { display: none !important; }