/** * typing.js * Typing animation for .hero-role / #typed element */ export function initTyping() { 'use strict'; const typedElement = document.getElementById('typed'); if (!typedElement) return; const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; const phrasesJson = typedElement.getAttribute('data-phrases'); let phrases = []; if (phrasesJson) { try { phrases = JSON.parse(phrasesJson); } catch (e) { console.warn('Failed to parse typing phrases:', e); phrases = ['Security & Web Dev', 'WordPress Developer']; } } if (!phrases.length) return; let currentPhraseIndex = 0; let currentCharIndex = 0; let isDeleting = false; function type() { const phrase = phrases[currentPhraseIndex]; const speed = isDeleting ? 50 : 100; if (isDeleting) { currentCharIndex--; } else { currentCharIndex++; } typedElement.textContent = phrase.substring(0, currentCharIndex); // Add cursor if (!isDeleting && currentCharIndex === phrase.length) { typedElement.innerHTML += ''; } else if (isDeleting && currentCharIndex === 0) { currentPhraseIndex = (currentPhraseIndex + 1) % phrases.length; isDeleting = false; setTimeout(type, 500); return; } else { typedElement.innerHTML = phrase.substring(0, currentCharIndex) + ''; } if (!isDeleting && currentCharIndex === phrase.length) { isDeleting = true; setTimeout(type, 2000); } else { setTimeout(type, prefersReducedMotion ? 0 : speed); } } if (prefersReducedMotion) { // Just show the first phrase without animation typedElement.textContent = phrases[0]; } else { type(); } }