fix(frontend): add hero tilt animation and stronger landing parity

This commit is contained in:
Ashwin Kumar 2026-03-17 00:35:37 +01:00
parent 973ec59b69
commit e3b857a767
2 changed files with 29 additions and 2 deletions

View file

@ -930,6 +930,8 @@ body {
.lp-hero-slider { .lp-hero-slider {
position: relative; position: relative;
min-height: 370px; min-height: 370px;
transition: transform 260ms ease;
will-change: transform;
} }
.lp-float-card { .lp-float-card {

View file

@ -239,6 +239,8 @@ export default function PublicLanding() {
const [scrolled, setScrolled] = createSignal(false); const [scrolled, setScrolled] = createSignal(false);
const [mobileOpen, setMobileOpen] = createSignal(false); const [mobileOpen, setMobileOpen] = createSignal(false);
const [scrollY, setScrollY] = createSignal(0); const [scrollY, setScrollY] = createSignal(0);
const [reduceMotion, setReduceMotion] = createSignal(false);
const [heroTilt, setHeroTilt] = createSignal({ x: 0, y: 0 });
const [heroIdx, setHeroIdx] = createSignal(0); const [heroIdx, setHeroIdx] = createSignal(0);
const [filter, setFilter] = createSignal<'all' | 'customer' | 'professional' | 'company' | 'job_seeker'>('all'); const [filter, setFilter] = createSignal<'all' | 'customer' | 'professional' | 'company' | 'job_seeker'>('all');
const [pathPage, setPathPage] = createSignal(0); const [pathPage, setPathPage] = createSignal(0);
@ -249,6 +251,10 @@ export default function PublicLanding() {
const [openFaq, setOpenFaq] = createSignal(0); const [openFaq, setOpenFaq] = createSignal(0);
onMount(() => { onMount(() => {
const media = window.matchMedia('(prefers-reduced-motion: reduce)');
const syncMotion = () => setReduceMotion(media.matches);
syncMotion();
const onScroll = () => { const onScroll = () => {
setScrolled(window.scrollY > 10); setScrolled(window.scrollY > 10);
setScrollY(window.scrollY || 0); setScrollY(window.scrollY || 0);
@ -271,6 +277,7 @@ export default function PublicLanding() {
syncCardsPerPage(); syncCardsPerPage();
window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', syncCardsPerPage); window.addEventListener('resize', syncCardsPerPage);
media.addEventListener('change', syncMotion);
const heroTimer = window.setInterval(() => setHeroIdx((x) => (x + 1) % heroSlides.length), 3500); const heroTimer = window.setInterval(() => setHeroIdx((x) => (x + 1) % heroSlides.length), 3500);
const pathTimer = window.setInterval(() => setPathPage((x) => x + 1), 4200); const pathTimer = window.setInterval(() => setPathPage((x) => x + 1), 4200);
@ -288,6 +295,7 @@ export default function PublicLanding() {
onCleanup(() => { onCleanup(() => {
window.removeEventListener('scroll', onScroll); window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', syncCardsPerPage); window.removeEventListener('resize', syncCardsPerPage);
media.removeEventListener('change', syncMotion);
window.clearInterval(heroTimer); window.clearInterval(heroTimer);
window.clearInterval(pathTimer); window.clearInterval(pathTimer);
window.clearInterval(benefitTimer); window.clearInterval(benefitTimer);
@ -377,7 +385,17 @@ export default function PublicLanding() {
</Show> </Show>
</header> </header>
<section class="public-hero scene-dark"> <section
class="public-hero scene-dark"
onMouseMove={(event) => {
if (reduceMotion()) return;
const rect = event.currentTarget.getBoundingClientRect();
const px = (event.clientX - rect.left) / rect.width - 0.5;
const py = (event.clientY - rect.top) / rect.height - 0.5;
setHeroTilt({ x: px * 14, y: py * 12 });
}}
onMouseLeave={() => setHeroTilt({ x: 0, y: 0 })}
>
<div class="container lp-hero-grid"> <div class="container lp-hero-grid">
<div> <div>
<h1 class="lp-hero-title">Hire verified professionals. Post jobs. Get approvals in 24-48 hours.</h1> <h1 class="lp-hero-title">Hire verified professionals. Post jobs. Get approvals in 24-48 hours.</h1>
@ -389,7 +407,14 @@ export default function PublicLanding() {
<A class="btn ghost-dark" href="/onboarding?schemaId=jobseeker_onboarding_v1">Apply for Jobs</A> <A class="btn ghost-dark" href="/onboarding?schemaId=jobseeker_onboarding_v1">Apply for Jobs</A>
</div> </div>
</div> </div>
<div class="lp-hero-slider"> <div
class="lp-hero-slider"
style={{
transform: reduceMotion()
? 'none'
: `translate3d(${heroTilt().x}px, ${heroTilt().y}px, 0)`,
}}
>
<For each={heroSlides}> <For each={heroSlides}>
{(slide, idx) => ( {(slide, idx) => (
<article class={`lp-float-card ${idx() === heroIdx() ? 'active' : ''}`}> <article class={`lp-float-card ${idx() === heroIdx() ? 'active' : ''}`}>