Drop turtle, honor ?template= URL param, add warp easter egg
- Removed the cosmos turtle (CSS rules + auto-load probe + JS hook). Reduced-motion turtle branch removed too. - ?template=editorial|swiss|cosmos URL param now wins over the value in data/links.json (and the cached localStorage fallback). Wired in both the pre-paint boot script and main() so deep links work without flashing the wrong template. - New hidden easter egg: type the letters w-a-r-p anywhere on the page (no input focused) and the page reloads on the next template in the cycle editorial -> swiss -> cosmos -> editorial. The URL is updated with the new ?template= param so the override survives refresh and is shareable. A small "↯ <name>" toast pops up after the reload via sessionStorage handoff, fades after ~2.5s, respects prefers-reduced-motion. - README documents both the URL param and the warp keyword.
This commit is contained in:
15
README.md
15
README.md
@@ -439,6 +439,21 @@ To switch:
|
||||
Choice is also cached in `localStorage` (`dlstack-template`) so reloads
|
||||
don't flash the wrong template before JSON parses.
|
||||
|
||||
### Overriding the template at view time
|
||||
|
||||
Two ways to ride a different template without touching `links.json`:
|
||||
|
||||
1. **URL parameter** — `?template=editorial`, `?template=swiss`, or
|
||||
`?template=cosmos` always wins over the JSON value, so links like
|
||||
`https://your.site/?template=cosmos` are shareable.
|
||||
2. **Easter egg** — type `warp` anywhere on the page (no input field
|
||||
focused). The page reloads on the next template in the cycle and a
|
||||
small `↯ <name>` toast confirms it. The URL is updated with the new
|
||||
`?template=` param.
|
||||
|
||||
Drop the URL param (or refresh from a clean URL) to return to whatever
|
||||
`theme.template` says in `data/links.json`.
|
||||
|
||||
## Theme (light / dark / auto)
|
||||
|
||||
A small pill in the top-right cycles **Auto → Light → Dark → Auto** on
|
||||
|
||||
@@ -153,43 +153,6 @@
|
||||
transition: transform 600ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
/* galactic space turtle — floats in from the left, dips, then back out */
|
||||
:root[data-template="cosmos"] .cosmos-turtle {
|
||||
position: fixed;
|
||||
top: clamp(48px, 9vh, 110px);
|
||||
left: 0;
|
||||
width: clamp(140px, 16vw, 240px);
|
||||
height: auto;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
filter:
|
||||
drop-shadow(0 0 22px rgba(122, 247, 255, 0.55))
|
||||
drop-shadow(0 0 48px rgba(176, 122, 255, 0.45))
|
||||
drop-shadow(0 0 80px rgba(255, 78, 205, 0.25));
|
||||
animation:
|
||||
cosmos-turtle-cycle 28s 2s linear infinite,
|
||||
cosmos-turtle-bob 4s ease-in-out infinite;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
@keyframes cosmos-turtle-cycle {
|
||||
0% { transform: translate(-25vw, 0) rotate(-6deg); opacity: 0; }
|
||||
6% { opacity: 1; }
|
||||
45% { transform: translate(45vw, -4vh) rotate(4deg); opacity: 1; }
|
||||
88% { transform: translate(112vw, 0) rotate(8deg); opacity: 1; }
|
||||
93% { opacity: 0; }
|
||||
100% { transform: translate(112vw, 0) rotate(8deg); opacity: 0; }
|
||||
}
|
||||
@keyframes cosmos-turtle-bob {
|
||||
0%, 100% { filter:
|
||||
drop-shadow(0 0 22px rgba(122, 247, 255, 0.55))
|
||||
drop-shadow(0 0 48px rgba(176, 122, 255, 0.45))
|
||||
drop-shadow(0 0 80px rgba(255, 78, 205, 0.25)); }
|
||||
50% { filter:
|
||||
drop-shadow(0 0 32px rgba(122, 247, 255, 0.75))
|
||||
drop-shadow(0 0 64px rgba(176, 122, 255, 0.60))
|
||||
drop-shadow(0 0 110px rgba(255, 78, 205, 0.40)); }
|
||||
}
|
||||
|
||||
/* CSS-only static fallback nebula when WebGL is unavailable */
|
||||
:root[data-template="cosmos"].cosmos-static body {
|
||||
@@ -775,18 +738,13 @@
|
||||
:root[data-template="cosmos"] .hero__asterism,
|
||||
:root[data-template="cosmos"] .section__head::after,
|
||||
:root[data-template="cosmos"] .card::before,
|
||||
:root[data-template="cosmos"] .cosmos-comet,
|
||||
:root[data-template="cosmos"] .cosmos-turtle {
|
||||
:root[data-template="cosmos"] .cosmos-comet {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
:root[data-template="cosmos"] .cosmos-bg,
|
||||
:root[data-template="cosmos"] .cosmos-halo,
|
||||
:root[data-template="cosmos"] .cosmos-comet { display: none; }
|
||||
:root[data-template="cosmos"] .cosmos-turtle {
|
||||
opacity: 1;
|
||||
transform: translate(2vw, 0) rotate(-2deg);
|
||||
}
|
||||
:root[data-template="cosmos"] .hero__name,
|
||||
:root[data-template="cosmos"] .hero__tagline,
|
||||
:root[data-template="cosmos"] .hero__asterism { transform: none !important; }
|
||||
|
||||
@@ -696,3 +696,33 @@ body::before {
|
||||
}
|
||||
|
||||
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
|
||||
|
||||
/* warp toast — surfaces the active template after the easter egg fires */
|
||||
.warp-toast {
|
||||
position: fixed;
|
||||
bottom: clamp(1.25rem, 3vw, 2.25rem);
|
||||
left: 50%;
|
||||
z-index: 1000;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
padding: 0.6rem 1.1rem;
|
||||
background: var(--ink);
|
||||
color: var(--paper);
|
||||
font: 600 var(--fs-sm)/1 var(--mono);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.18em;
|
||||
border-radius: 999px;
|
||||
box-shadow: 0 12px 30px -16px rgba(0, 0, 0, 0.5);
|
||||
opacity: 0;
|
||||
transform: translate(-50%, 18px);
|
||||
animation: warp-in 360ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
||||
pointer-events: none;
|
||||
}
|
||||
.warp-toast__arrow { color: var(--accent); font-size: 1.1em; }
|
||||
.warp-toast--out { animation: warp-out 600ms ease forwards; }
|
||||
@keyframes warp-in { to { opacity: 1; transform: translate(-50%, 0); } }
|
||||
@keyframes warp-out { to { opacity: 0; transform: translate(-50%, 18px); } }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.warp-toast { animation: none; opacity: 1; transform: translate(-50%, 0); }
|
||||
}
|
||||
|
||||
@@ -284,10 +284,9 @@
|
||||
});
|
||||
}
|
||||
|
||||
function bootCosmos(theme) {
|
||||
function bootCosmos() {
|
||||
const root = document.documentElement;
|
||||
const reducedMotion = matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
attachCosmosTurtle(theme);
|
||||
if (reducedMotion) { root.classList.add("cosmos-static"); return; }
|
||||
attachCosmosCursor();
|
||||
attachCosmosComets();
|
||||
@@ -566,19 +565,44 @@
|
||||
});
|
||||
}
|
||||
|
||||
function attachCosmosTurtle(theme) {
|
||||
const src = (theme && theme.turtle) || "assets/img/turtle.png";
|
||||
const probe = new Image();
|
||||
probe.onload = () => {
|
||||
const t = document.createElement("img");
|
||||
t.src = src;
|
||||
t.alt = "";
|
||||
t.className = "cosmos-turtle";
|
||||
t.setAttribute("aria-hidden", "true");
|
||||
document.body.appendChild(t);
|
||||
};
|
||||
probe.onerror = () => {};
|
||||
probe.src = src;
|
||||
function attachWarpEgg() {
|
||||
const order = ["editorial", "swiss", "cosmos"];
|
||||
let buf = "";
|
||||
const KEY = "warp";
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
const tgt = e.target;
|
||||
if (tgt && tgt.matches && tgt.matches("input, textarea, [contenteditable]")) return;
|
||||
if (!e.key || e.key.length !== 1) return;
|
||||
buf = (buf + e.key.toLowerCase()).slice(-KEY.length);
|
||||
if (buf !== KEY) return;
|
||||
buf = "";
|
||||
const cur = document.documentElement.dataset.template || "editorial";
|
||||
const next = order[(order.indexOf(cur) + 1) % order.length];
|
||||
try { sessionStorage.setItem("dlstack-warp-toast", next); } catch (err) {}
|
||||
const url = new URL(location.href);
|
||||
url.searchParams.set("template", next);
|
||||
location.replace(url.toString());
|
||||
});
|
||||
|
||||
let pending = null;
|
||||
try { pending = sessionStorage.getItem("dlstack-warp-toast"); } catch (err) {}
|
||||
if (!pending) return;
|
||||
try { sessionStorage.removeItem("dlstack-warp-toast"); } catch (err) {}
|
||||
const toast = document.createElement("div");
|
||||
toast.className = "warp-toast";
|
||||
toast.setAttribute("role", "status");
|
||||
const arrow = document.createElement("span");
|
||||
arrow.className = "warp-toast__arrow";
|
||||
arrow.setAttribute("aria-hidden", "true");
|
||||
arrow.textContent = "↯";
|
||||
const label = document.createElement("span");
|
||||
label.className = "warp-toast__label";
|
||||
label.textContent = pending;
|
||||
toast.append(arrow, label);
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(() => toast.classList.add("warp-toast--out"), 1600);
|
||||
setTimeout(() => toast.remove(), 2500);
|
||||
}
|
||||
|
||||
function attachCarousels(root) {
|
||||
@@ -676,10 +700,14 @@
|
||||
document.documentElement.style.setProperty("--accent", data.theme.accent);
|
||||
}
|
||||
const validTpl = new Set(["editorial", "swiss", "cosmos"]);
|
||||
const tpl = validTpl.has(data.theme?.template) ? data.theme.template : "editorial";
|
||||
let urlTpl = null;
|
||||
try { urlTpl = new URL(location.href).searchParams.get("template"); } catch (e) {}
|
||||
const tpl = validTpl.has(urlTpl) ? urlTpl
|
||||
: validTpl.has(data.theme?.template) ? data.theme.template
|
||||
: "editorial";
|
||||
document.documentElement.dataset.template = tpl;
|
||||
try { localStorage.setItem("dlstack-template", tpl); } catch (e) {}
|
||||
if (tpl === "cosmos") bootCosmos(data.theme || {});
|
||||
if (tpl === "cosmos") bootCosmos();
|
||||
|
||||
const p = data.profile || {};
|
||||
const sections = data.sections || [];
|
||||
@@ -719,6 +747,7 @@
|
||||
attachCarousels(app);
|
||||
attachReveal(app);
|
||||
attachTheme();
|
||||
attachWarpEgg();
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
|
||||
@@ -22,12 +22,19 @@
|
||||
<link href="https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600;700;900&family=DM+Serif+Display:ital@0;1&family=Fraunces:opsz,wght,SOFT,WONK@9..144,300..600,30..100,0..1&family=Geist:wght@300;400;500;600&family=Geist+Mono:wght@400;500&family=Orbitron:wght@400;500;700;900&display=swap" rel="stylesheet">
|
||||
|
||||
<script>
|
||||
// Apply saved theme + template before paint to avoid flash
|
||||
// Apply saved theme + template before paint to avoid flash.
|
||||
// URL ?template=… wins over the cached value so deep links work.
|
||||
try {
|
||||
var t = localStorage.getItem("dlstack-theme");
|
||||
if (t === "light" || t === "dark") document.documentElement.dataset.theme = t;
|
||||
var qtpl = null;
|
||||
try { qtpl = new URL(location.href).searchParams.get("template"); } catch (e) {}
|
||||
if (qtpl === "swiss" || qtpl === "editorial" || qtpl === "cosmos") {
|
||||
document.documentElement.dataset.template = qtpl;
|
||||
} else {
|
||||
var tpl = localStorage.getItem("dlstack-template");
|
||||
if (tpl === "swiss" || tpl === "editorial" || tpl === "cosmos") document.documentElement.dataset.template = tpl;
|
||||
}
|
||||
} catch (e) {}
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user