mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 22:26:38 +03:00
Restyle the live picker to match the site kinpaku kit, persist pick mode in localStorage, fix DESIGN.md color swatches in the parser, and land the neo-kinpaku site refresh with new tokens, assets, palette script, and detector rules. Co-authored-by: Cursor <cursoragent@cursor.com>
119 lines
3.7 KiB
JavaScript
119 lines
3.7 KiB
JavaScript
// Instant anchor scroll - no smooth scrolling for better UX on long pages.
|
|
// `behavior: 'instant'` explicitly overrides any CSS `scroll-behavior: smooth`
|
|
// from a stylesheet we don't own; `behavior: 'auto'` would defer to CSS.
|
|
export function initAnchorScroll() {
|
|
const getAnchorOffset = () => {
|
|
const header = document.querySelector('[data-site-header]');
|
|
return (header?.getBoundingClientRect().height || 0) + 28;
|
|
};
|
|
|
|
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
|
|
anchor.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
const target = document.querySelector(anchor.getAttribute("href"));
|
|
if (target) {
|
|
const offset = getAnchorOffset();
|
|
const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
|
|
window.scrollTo({ top: targetPosition, behavior: 'instant' });
|
|
}
|
|
});
|
|
});
|
|
|
|
return getAnchorOffset;
|
|
}
|
|
|
|
export function initHashTracking() {
|
|
const sections = document.querySelectorAll('section[id]');
|
|
if (!sections.length) return;
|
|
|
|
const getAnchorOffset = () => {
|
|
const header = document.querySelector('[data-site-header]');
|
|
return (header?.getBoundingClientRect().height || 0) + 28;
|
|
};
|
|
|
|
let currentHash = window.location.hash.slice(1) || '';
|
|
let ticking = false;
|
|
|
|
function updateHash() {
|
|
// Don't override command deep links while user is in the commands section
|
|
if (currentHash.startsWith('cmd-')) {
|
|
const cmdEl = document.getElementById(currentHash);
|
|
if (cmdEl) {
|
|
const rect = cmdEl.getBoundingClientRect();
|
|
// Only clear the cmd hash if user scrolled well away from commands section
|
|
if (rect.top > window.innerHeight * 2 || rect.bottom < -window.innerHeight) {
|
|
currentHash = '';
|
|
} else {
|
|
ticking = false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
const scrollY = window.scrollY;
|
|
const viewportHeight = window.innerHeight;
|
|
const triggerPoint = scrollY + viewportHeight * 0.3;
|
|
|
|
let activeSection = '';
|
|
|
|
sections.forEach(section => {
|
|
const rect = section.getBoundingClientRect();
|
|
const sectionTop = scrollY + rect.top;
|
|
const sectionBottom = sectionTop + rect.height;
|
|
|
|
if (triggerPoint >= sectionTop && triggerPoint < sectionBottom) {
|
|
activeSection = section.id;
|
|
}
|
|
});
|
|
|
|
// Don't set #hero — it's the default state, no hash needed
|
|
if (activeSection === 'hero') activeSection = '';
|
|
|
|
if (activeSection !== currentHash) {
|
|
currentHash = activeSection;
|
|
if (activeSection) {
|
|
history.replaceState(null, '', `#${activeSection}`);
|
|
} else {
|
|
history.replaceState(null, '', window.location.pathname);
|
|
}
|
|
}
|
|
|
|
ticking = false;
|
|
}
|
|
|
|
window.addEventListener('scroll', () => {
|
|
if (!ticking) {
|
|
requestAnimationFrame(updateHash);
|
|
ticking = true;
|
|
}
|
|
}, { passive: true });
|
|
|
|
// Handle initial hash on page load — instant jump, retried on
|
|
// fonts.ready and window `load`. A fixed setTimeout is unreliable
|
|
// because async-loaded display fonts reflow the page by hundreds of
|
|
// pixels when they swap in; computing target position before that
|
|
// lands the user several sections above the right spot.
|
|
if (window.location.hash) {
|
|
const hash = window.location.hash.slice(1);
|
|
const target = document.getElementById(hash);
|
|
if (target) {
|
|
currentHash = hash;
|
|
let clicked = false;
|
|
const jump = () => {
|
|
const offset = getAnchorOffset();
|
|
const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
|
|
window.scrollTo({ top: targetPosition, behavior: 'instant' });
|
|
if (!clicked && hash.startsWith('cmd-') && target.classList.contains('manual-entry')) {
|
|
target.click();
|
|
clicked = true;
|
|
}
|
|
};
|
|
jump();
|
|
if (document.fonts?.ready) document.fonts.ready.then(jump).catch(() => {});
|
|
window.addEventListener('load', jump, { once: true });
|
|
}
|
|
} else {
|
|
// No hash — don't set one on initial load
|
|
}
|
|
}
|