mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 06:06:37 +03:00
New commands:
- /teach-impeccable: One-time setup that gathers UX-focused design
context (explores codebase first, then asks targeted questions)
- /review: UX design review evaluating hierarchy, clarity, emotional
resonance (complements technical /audit)
Build system improvements:
- Dynamic {{model}}, {{ask_instruction}}, {{config_file}} placeholders
per provider (Claude/Gemini/GPT/the model)
- context: fork support for Claude Code sub-agents (audit, extract)
- Skill reference auto-added to design commands
Skill refinements:
- Merged Design Thinking and Context Awareness sections
- Removed redundant patterns and technical questions
- More direct feedback guidance (radical candor)
Website updates:
- 17 commands (was 16)
- Review demo showing UX issues (HIERARCHY, NO PRIMARY, DEAD END)
- teach-impeccable in System group of periodic table
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
126 lines
3.0 KiB
JavaScript
126 lines
3.0 KiB
JavaScript
import Lenis from "lenis";
|
|
|
|
// Check if user prefers reduced motion
|
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
export function initSmoothScroll() {
|
|
// Skip smooth scroll entirely if user prefers reduced motion
|
|
if (prefersReducedMotion) {
|
|
// Still handle anchor links but with instant scroll
|
|
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
|
|
anchor.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
const target = document.querySelector(anchor.getAttribute("href"));
|
|
if (target) {
|
|
target.scrollIntoView({ behavior: 'auto', block: 'start' });
|
|
}
|
|
});
|
|
});
|
|
return null;
|
|
}
|
|
|
|
const lenis = new Lenis({
|
|
duration: 1.2,
|
|
easing: (t) => Math.min(1, 1.001 - 2 ** (-10 * t)),
|
|
orientation: "vertical",
|
|
gestureOrientation: "vertical",
|
|
smoothWheel: true,
|
|
wheelMultiplier: 1,
|
|
touchMultiplier: 2,
|
|
});
|
|
|
|
function raf(time) {
|
|
lenis.raf(time);
|
|
requestAnimationFrame(raf);
|
|
}
|
|
|
|
requestAnimationFrame(raf);
|
|
|
|
// Handle anchor links
|
|
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
|
|
anchor.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
const target = document.querySelector(anchor.getAttribute("href"));
|
|
if (target) {
|
|
lenis.scrollTo(target, { offset: -40 });
|
|
}
|
|
});
|
|
});
|
|
|
|
return lenis;
|
|
}
|
|
|
|
export function initScrollIndicator() {
|
|
const indicator = document.querySelector(".hero-scroll-indicator");
|
|
if (!indicator) return;
|
|
|
|
function update() {
|
|
if (window.scrollY > 20) {
|
|
indicator.classList.add("hidden");
|
|
} else {
|
|
indicator.classList.remove("hidden");
|
|
}
|
|
}
|
|
|
|
window.addEventListener("scroll", update, { passive: true });
|
|
update();
|
|
}
|
|
|
|
export function initHashTracking() {
|
|
const sections = document.querySelectorAll('section[id]');
|
|
if (!sections.length) return;
|
|
|
|
let currentHash = window.location.hash.slice(1) || '';
|
|
let ticking = false;
|
|
|
|
function updateHash() {
|
|
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;
|
|
}
|
|
});
|
|
|
|
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
|
|
if (window.location.hash) {
|
|
const target = document.querySelector(window.location.hash);
|
|
if (target) {
|
|
setTimeout(() => {
|
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
// Initial check
|
|
updateHash();
|
|
}
|
|
|