1f58f8ccc7
- extend_footer.html: убран ранний return при prefers-reduced-motion — печать идёт всегда, мигание гасится только через CSS (кейфрейм tw-blink уже отключается) - typewriter.css: убрано двойное auto-поле (.logo margin-inline-end:auto конфликтовало с #menu margin-inline-start:auto при justify-content:space-between) — теперь только menu толкает вправо, солнышко встаёт сразу за меню
52 lines
2.1 KiB
HTML
52 lines
2.1 KiB
HTML
{{- /* dedinit() typewriter: animated topics inside logo parens.
|
|
Pure JS + CSS, no Alpine. Progressive enhancement — no-JS keeps
|
|
the plain "dedinit()" logo. Words configurable via hugo.toml:
|
|
[params.typewriter] words = ["linux", "llm", "politics"] */ -}}
|
|
{{- $tw_words := site.Params.typewriter.words | default (slice "linux" "llm" "politics" "homelab" "nix") -}}
|
|
<script>
|
|
(() => {
|
|
const LOGO_SEL = 'header .logo a, .logo a';
|
|
const queue = [() => {
|
|
const a = document.querySelector(LOGO_SEL);
|
|
if (!a || a.dataset.twDone) return;
|
|
a.dataset.twDone = '1';
|
|
const text = a.textContent.trim();
|
|
const open = text.lastIndexOf('(');
|
|
const close = text.lastIndexOf(')');
|
|
if (open < 0 || close < open) return;
|
|
const words = {{ $tw_words | jsonify | safeJS }};
|
|
const box = document.createElement('span');
|
|
box.id = 'dedinit-typewriter';
|
|
box.className = 'tw';
|
|
box.textContent = '';
|
|
box.appendChild(Object.assign(document.createElement('span'), { className: 'tw-cursor' }));
|
|
// rebuild: <prefix>(<typewriter>)<suffix>
|
|
a.textContent = '';
|
|
a.appendChild(document.createTextNode(text.slice(0, open + 1)));
|
|
a.appendChild(box);
|
|
a.appendChild(document.createTextNode(text.slice(close)));
|
|
|
|
// reduced motion: blink is disabled via CSS; keep typing but no blink
|
|
const TYPE = 90, ERASE = 50, HOLD = 1400, GAP = 400;
|
|
let w = 0, i = 0, dir = 1;
|
|
box.querySelector('.tw-cursor').style.display = 'inline-block';
|
|
const tick = () => {
|
|
const word = words[w];
|
|
// keep cursor as the last child; typed text goes before it
|
|
box.replaceChildren(document.createTextNode(word.slice(0, i)), box.querySelector('.tw-cursor'));
|
|
let delay;
|
|
if (dir === 1) {
|
|
if (i < word.length) { i++; delay = TYPE; }
|
|
else { dir = -1; delay = HOLD; }
|
|
} else {
|
|
if (i > 0) { i--; delay = ERASE; }
|
|
else { dir = 1; w = (w + 1) % words.length; delay = GAP; }
|
|
}
|
|
setTimeout(tick, delay);
|
|
};
|
|
tick();
|
|
}];
|
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => queue.shift()());
|
|
else queue.shift()();
|
|
})();
|
|
</script> |