New `type: "testimonial"` with structured attribution
(quote/name/role/org/url/image/date) plus a `layout: "testimonials"`
section behavior that renders the items as a tasteful crossfade
carousel — one quote at a time, 7s auto-advance, prev/next nav, dot
indicators, swipe support, ←/→ keys, pause on hover/focus, ARIA
live region.
Reduced-motion users automatically get .carousel--stacked: every
testimonial visible at once, controls hidden, no auto-advance. A
single-item section also skips the carousel chrome.
Per-template treatment: editorial uses Fraunces italic for the curly
quote mark and the body, swiss strips uppercase from titles per prior
fix, cosmos glows the mark with the cyan/violet accent stack.
Section auto-detects the layout when the first item is a testimonial,
matching how the clients layout already works.
Third theme template scoped to :root[data-template="cosmos"], joining
editorial and swiss. Set "theme.template": "cosmos" in links.json to
activate.
Background — full-viewport WebGL fragment shader running a 5-octave
warped fBm nebula in indigo/violet/magenta/cyan, with a two-layer
procedural starfield (small dense + sparse blue-white halo) twinkling
on independent phases. Pointer-tracking gravity well subtly perturbs
sampling around the cursor. Pixel ratio capped at 1.5x for fill-rate;
loop pauses on visibility change.
UI — Orbitron display + Geist body, glassmorphic cards with
backdrop-filter, conic-gradient holographic borders driven by
@property --cosmos-hue rotating on hover, gradient-clip section
numbers and project titles, chromatic-split hero last name on hover
(::before + ::after pseudos with mix-blend-mode screen using a
data-text attr), glowing drift-orb in place of the editorial asterism,
animated section-head accent line.
Fallbacks — prefers-reduced-motion or WebGL-unavailable triggers the
.cosmos-static class with a CSS-only radial-gradient nebula and all
heavy animations disabled. Always-dark regardless of theme toggle.
README documents the new template; links.example.json unchanged.
The .yt::after gradient overlay was sitting on top of the inserted
iframe and stealing every click — clicks on the YouTube player controls
bubbled to the .yt facade, which still matched [data-yt] and re-ran
open(), replacing the iframe and restarting playback from 0.
- Add pointer-events:none on .yt::after so it never blocks player clicks
- On play, swap .yt to a .yt--playing state that hides the overlay,
play button, and title overlay
- Remove the data-yt/role/tabindex attrs after init so any leftover
clicks on the facade no longer match the open() delegate
- Add controls=1 explicitly and broaden the allow list (clipboard-write,
web-share) for the embedded player
Swiss: drop all-caps on titles, soften heavy ink borders to muted rules
or remove them where background contrast carries the form, balance hero
name weights (smaller lighter first name vs heavier italic last name),
fix unreadable white description text on portfolio hover.
Editorial: enlarge the section "No" mark and set it in italic Fraunces so
it reads as typography instead of a tick, swap the first name into DM
Serif Display for a different J, sharpen the section number caption.
Client tile: contain custom logos at 86% so wide marks like
"The Cooperative Way" stop getting cropped to "operative".
New: optional `date` field on every item type (link/card/portfolio/
youtube/client). Accepts YYYY, YYYY-MM, or YYYY-MM-DD. Rendered
human-friendly with the raw ISO preserved in <time datetime>.
Documented in README and demonstrated in links.example.json.
A hand-rolled linktree alternative — pure static HTML/CSS/JS, no build
step. Drop on any shared host via SFTP and edit data/links.json to update.
Features
- File-driven content via data/links.json (links, projects, YouTube,
client tiles, portfolio pieces)
- Two interchangeable templates: editorial (Fraunces + paper + vermilion)
and swiss (Archivo grotesque, all-caps poster)
- Auto/light/dark theme toggle with no-flash boot script
- Auto-fetched favicons via Google S2 (with image-URL override)
- Lazy YouTube facades (no third-party JS until clicked)
- Adaptive client-logo grid
- Scroll-triggered reveal animations
- ~40 KB total payload, ~12 KB gzipped
The repo ships links.example.json as a demo; data/links.json is
gitignored so personal content stays out of the public repo.