diff --git a/index.html b/index.html index d16685b..80b227f 100644 --- a/index.html +++ b/index.html @@ -54,7 +54,6 @@
-

// Hello, world. I'm

Nikolaj Gade

Software Developer

they/them · he/him

diff --git a/script.js b/script.js index 4ff9c62..cae4014 100644 --- a/script.js +++ b/script.js @@ -1,14 +1,55 @@ +// ── Hero typewriter ──────────────────────────────────────────── +function startHeroTypewriter(nameEl, fullName, restEls) { + const cursor = document.createElement('span'); + cursor.className = 'hero-cursor'; + nameEl.appendChild(cursor); + + let i = 0; + function typeNext() { + if (i < fullName.length) { + nameEl.insertBefore(document.createTextNode(fullName[i]), cursor); + i++; + setTimeout(typeNext, 95); + } else { + // Cursor done — fade it out, then reveal the rest + cursor.style.transition = 'opacity 0.3s'; + cursor.style.opacity = '0'; + setTimeout(() => { + cursor.remove(); + restEls.forEach((el, idx) => { + setTimeout(() => el.classList.add('visible'), idx * 110); + }); + }, 0); + } + } + typeNext(); +} + // ── Loader ──────────────────────────────────────────────────── (function () { - const loader = document.getElementById('loader'); - const pctEl = document.getElementById('loaderPct'); + const loader = document.getElementById('loader'); + const pctEl = document.getElementById('loaderPct'); + const nameEl = document.querySelector('.hero-name'); + const restEls = [...document.querySelectorAll('.hero-role, .hero-pronouns, .hero-links, .hero-visual')]; + + // Store the name and clear it immediately (loader covers the page anyway) + const fullName = nameEl.textContent.trim(); + nameEl.textContent = ''; + + // Hide hero elements that reveal after typing + restEls.forEach(el => el.classList.add('fade-up')); + let pct = 0; const tick = setInterval(() => { - pct += Math.random() * 20; + pct += Math.random() * 15; if (pct >= 100) { pct = 100; clearInterval(tick); - setTimeout(() => loader.classList.add('hide'), 300); + setTimeout(() => { + loader.classList.add('hide'); + // Wait for the loader fade-out (0.6s), then start typing + setTimeout(() => startHeroTypewriter(nameEl, fullName, restEls), 600); + }, 300); } pctEl.textContent = Math.floor(pct) + '%'; }, 110); @@ -166,7 +207,7 @@ (function () { const targets = document.querySelectorAll( '.section-header, .about-grid, .skill-card, .timeline-item, ' + - '.project-card, .edu-card, .hero-content, .hero-visual' + '.project-card, .edu-card' ); targets.forEach(el => el.classList.add('fade-up')); diff --git a/style.css b/style.css index d21b901..bf8bd1d 100644 --- a/style.css +++ b/style.css @@ -71,7 +71,7 @@ button { font-family: var(--font); cursor: pointer; border: none; background: no @keyframes lcFade { to { opacity: 1; transform: none; } } .loader-bar-wrap { width: 200px; height: 3px; background: rgba(255,255,255,0.1); border-radius: 99px; margin: 0 auto 0.75rem; overflow: hidden; } -.loader-bar { height: 100%; background: var(--grad); width: 0%; animation: barFill 2.2s var(--ease) forwards; animation-delay: 0.5s; } +.loader-bar { height: 100%; background: var(--grad); width: 0%; animation: barFill 2.5s var(--ease) forwards; animation-delay: 0.5s; } @keyframes barFill { to { width: 100%; } } .loader-pct { font-family: var(--mono); font-size: 0.8rem; color: var(--orange); } @@ -165,11 +165,27 @@ main { position: relative; z-index: 1; } .fade-up { opacity: 0; transform: translateY(28px); transition: opacity 0.6s var(--ease), transform 0.6s var(--ease); } .fade-up.visible { opacity: 1; transform: none; } +/* Hero typewriter cursor */ +.hero-cursor { + display: inline-block; + width: 5px; + height: 0.8em; + background: var(--ink); + margin-left: 5px; + vertical-align: baseline; + border-radius: 1px; + animation: cursorBlink 0.7s step-end infinite; +} +@keyframes cursorBlink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} + /* ── Hero ───────────────────────────────────────────────────── */ .hero { min-height: 100vh; display: flex; align-items: center; - padding: calc(var(--nav-h) + 1rem) 2rem 15rem; + padding: calc(var(--nav-h) + 1rem) 2rem 10rem; max-width: var(--max-w); margin: 0 auto 250px auto; gap: 4rem; @@ -184,6 +200,7 @@ main { position: relative; z-index: 1; } font-weight: 900; line-height: 1; letter-spacing: -0.03em; + margin-top: 3rem; margin-bottom: 0.75rem; } .hero-role {