Build dylanmazzei.com redesign as a static multi-page site
Eight pages (home, about, portfolio, consulting, marketing, business setup, contact, press) with copy taken verbatim from the content documents, a shared layout, and a zero-dependency Node build ready for Vercel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
(function () {
|
||||
window.__site = true;
|
||||
var root = document.documentElement;
|
||||
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
// Nav border on scroll
|
||||
var nav = document.getElementById('nav');
|
||||
var onScroll = function () { nav && nav.classList.toggle('is-scrolled', window.scrollY > 8); };
|
||||
onScroll();
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
|
||||
// Mobile menu
|
||||
var menuBtn = document.getElementById('menu-btn');
|
||||
var menu = document.getElementById('mobile-menu');
|
||||
if (menuBtn && menu) {
|
||||
menuBtn.addEventListener('click', function () {
|
||||
var open = menu.hidden;
|
||||
menu.hidden = !open;
|
||||
menuBtn.setAttribute('aria-expanded', String(open));
|
||||
menuBtn.setAttribute('aria-label', open ? 'Close menu' : 'Open menu');
|
||||
});
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape' && !menu.hidden) { menu.hidden = true; menuBtn.setAttribute('aria-expanded', 'false'); menuBtn.focus(); }
|
||||
});
|
||||
}
|
||||
|
||||
// Reveal on scroll
|
||||
var reveals = document.querySelectorAll('.reveal');
|
||||
if ('IntersectionObserver' in window && !reduce) {
|
||||
var io = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (en) {
|
||||
if (en.isIntersecting) { en.target.classList.add('is-in'); io.unobserve(en.target); }
|
||||
});
|
||||
}, { rootMargin: '0px 0px -8% 0px', threshold: 0.05 });
|
||||
reveals.forEach(function (el) { io.observe(el); });
|
||||
} else {
|
||||
reveals.forEach(function (el) { el.classList.add('is-in'); });
|
||||
}
|
||||
|
||||
// Count-up numbers: <b data-count="8" data-suffix="+">8+</b>
|
||||
var counters = document.querySelectorAll('[data-count]');
|
||||
var run = function (el) {
|
||||
var end = parseInt(el.getAttribute('data-count'), 10);
|
||||
var suffix = el.getAttribute('data-suffix') || '';
|
||||
var start = null;
|
||||
var dur = 1200;
|
||||
var step = function (t) {
|
||||
if (!start) start = t;
|
||||
var p = Math.min((t - start) / dur, 1);
|
||||
var eased = 1 - Math.pow(1 - p, 3);
|
||||
el.textContent = Math.round(end * eased) + suffix;
|
||||
if (p < 1) requestAnimationFrame(step);
|
||||
};
|
||||
requestAnimationFrame(step);
|
||||
};
|
||||
if ('IntersectionObserver' in window && !reduce) {
|
||||
var co = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (en) { if (en.isIntersecting) { run(en.target); co.unobserve(en.target); } });
|
||||
}, { threshold: 0.6 });
|
||||
counters.forEach(function (el) { co.observe(el); });
|
||||
}
|
||||
|
||||
// Tabs (service lines)
|
||||
document.querySelectorAll('[data-tabs]').forEach(function (wrap) {
|
||||
var btns = wrap.querySelectorAll('[role="tab"]');
|
||||
var panels = wrap.querySelectorAll('[role="tabpanel"]');
|
||||
var select = function (i, focus) {
|
||||
btns.forEach(function (b, j) {
|
||||
var on = i === j;
|
||||
b.setAttribute('aria-selected', String(on));
|
||||
b.tabIndex = on ? 0 : -1;
|
||||
panels[j].hidden = !on;
|
||||
});
|
||||
if (focus) btns[i].focus();
|
||||
};
|
||||
btns.forEach(function (b, i) {
|
||||
b.addEventListener('click', function () { select(i); });
|
||||
b.addEventListener('keydown', function (e) {
|
||||
var k = e.key;
|
||||
if (k === 'ArrowDown' || k === 'ArrowRight') { e.preventDefault(); select((i + 1) % btns.length, true); }
|
||||
if (k === 'ArrowUp' || k === 'ArrowLeft') { e.preventDefault(); select((i - 1 + btns.length) % btns.length, true); }
|
||||
});
|
||||
});
|
||||
var hash = location.hash.replace('#', '');
|
||||
var start = 0;
|
||||
panels.forEach(function (p, j) { if (p.id === hash) start = j; });
|
||||
select(start);
|
||||
});
|
||||
|
||||
// Copy buttons
|
||||
document.querySelectorAll('[data-copy]').forEach(function (b) {
|
||||
b.addEventListener('click', function () {
|
||||
var text = b.getAttribute('data-copy');
|
||||
var label = b.textContent;
|
||||
var done = function () { b.textContent = 'Copied'; setTimeout(function () { b.textContent = label; }, 1600); };
|
||||
try { navigator.clipboard.writeText(text).then(done, function () {}); } catch (e) {}
|
||||
});
|
||||
});
|
||||
|
||||
// Inquiry form: opens the visitor's email client with the message prefilled.
|
||||
var form = document.getElementById('inquiry');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
if (!form.reportValidity()) return;
|
||||
var d = new FormData(form);
|
||||
var body = [
|
||||
'Name: ' + d.get('name'),
|
||||
'Email: ' + d.get('email'),
|
||||
d.get('company') ? 'Company: ' + d.get('company') : '',
|
||||
d.get('topic') ? 'Topic: ' + d.get('topic') : '',
|
||||
'',
|
||||
d.get('message')
|
||||
].filter(function (l, i) { return l !== '' || i === 4; }).join('\n');
|
||||
var subject = 'Inquiry from ' + d.get('name') + (d.get('topic') ? ' — ' + d.get('topic') : '');
|
||||
window.location.href = 'mailto:' + form.getAttribute('data-to') + '?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body);
|
||||
var status = document.getElementById('form-status');
|
||||
if (status) status.textContent = 'Your email app should open with the message ready to send.';
|
||||
});
|
||||
}
|
||||
|
||||
root.classList.add('ready');
|
||||
})();
|
||||
Reference in New Issue
Block a user