🐐
This commit is contained in:
@@ -54,7 +54,6 @@
|
|||||||
<!-- HERO -->
|
<!-- HERO -->
|
||||||
<section id="home" class="hero">
|
<section id="home" class="hero">
|
||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<p class="hero-greeting">// Hello, world. I'm</p>
|
|
||||||
<h1 class="hero-name" >Nikolaj Gade</h1>
|
<h1 class="hero-name" >Nikolaj Gade</h1>
|
||||||
<p class="hero-role">Software Developer</p>
|
<p class="hero-role">Software Developer</p>
|
||||||
<p class="hero-pronouns">they/them · he/him</p>
|
<p class="hero-pronouns">they/them · he/him</p>
|
||||||
|
|||||||
51
script.js
51
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 ────────────────────────────────────────────────────
|
// ── Loader ────────────────────────────────────────────────────
|
||||||
(function () {
|
(function () {
|
||||||
const loader = document.getElementById('loader');
|
const loader = document.getElementById('loader');
|
||||||
const pctEl = document.getElementById('loaderPct');
|
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;
|
let pct = 0;
|
||||||
const tick = setInterval(() => {
|
const tick = setInterval(() => {
|
||||||
pct += Math.random() * 20;
|
pct += Math.random() * 15;
|
||||||
if (pct >= 100) {
|
if (pct >= 100) {
|
||||||
pct = 100;
|
pct = 100;
|
||||||
clearInterval(tick);
|
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) + '%';
|
pctEl.textContent = Math.floor(pct) + '%';
|
||||||
}, 110);
|
}, 110);
|
||||||
@@ -166,7 +207,7 @@
|
|||||||
(function () {
|
(function () {
|
||||||
const targets = document.querySelectorAll(
|
const targets = document.querySelectorAll(
|
||||||
'.section-header, .about-grid, .skill-card, .timeline-item, ' +
|
'.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'));
|
targets.forEach(el => el.classList.add('fade-up'));
|
||||||
|
|
||||||
|
|||||||
21
style.css
21
style.css
@@ -71,7 +71,7 @@ button { font-family: var(--font); cursor: pointer; border: none; background: no
|
|||||||
@keyframes lcFade { to { opacity: 1; transform: none; } }
|
@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-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%; } }
|
@keyframes barFill { to { width: 100%; } }
|
||||||
.loader-pct { font-family: var(--mono); font-size: 0.8rem; color: var(--orange); }
|
.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 { opacity: 0; transform: translateY(28px); transition: opacity 0.6s var(--ease), transform 0.6s var(--ease); }
|
||||||
.fade-up.visible { opacity: 1; transform: none; }
|
.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 ───────────────────────────────────────────────────── */
|
||||||
.hero {
|
.hero {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: flex; align-items: center;
|
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);
|
max-width: var(--max-w);
|
||||||
margin: 0 auto 250px auto;
|
margin: 0 auto 250px auto;
|
||||||
gap: 4rem;
|
gap: 4rem;
|
||||||
@@ -184,6 +200,7 @@ main { position: relative; z-index: 1; }
|
|||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
letter-spacing: -0.03em;
|
letter-spacing: -0.03em;
|
||||||
|
margin-top: 3rem;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
.hero-role {
|
.hero-role {
|
||||||
|
|||||||
Reference in New Issue
Block a user