Files
gw-svelte/src/lib/components/HeroSection.svelte
T

181 lines
5.9 KiB
Svelte
Raw Normal View History

2026-05-02 08:26:18 +12:00
<script lang="ts">
import { onMount } from 'svelte';
2026-05-06 11:36:19 +12:00
import Icon from '$lib/components/Icon.svelte';
import type { CallToAction, HeroContent } from '$lib/types';
import { trackAb, type AbContext } from '$lib/ab';
2026-05-02 08:26:18 +12:00
export let hero: HeroContent;
2026-05-06 11:36:19 +12:00
export let reviewCta: CallToAction | undefined = undefined;
export let primaryCtaOverride: CallToAction | undefined = undefined;
export let ab: AbContext | undefined = undefined;
$: primaryCta = primaryCtaOverride ?? hero.primaryCta;
onMount(() => {
if (ab) trackAb({ ...ab, event_type: 'exposure', meta: { surface: 'hero' } });
});
function handlePrimaryCtaClick() {
if (ab) trackAb({ ...ab, event_type: 'cta_click', meta: { surface: 'hero_primary' } });
}
2026-05-02 08:26:18 +12:00
$: titleParts = splitTitle(hero.title);
$: mobileTitle = hero.mobileTitle?.trim() || `${hero.title} ${hero.highlight}`.trim();
2026-05-13 00:34:34 +12:00
$: mobileLead = mobileTitle.includes(hero.highlight)
? mobileTitle.slice(0, mobileTitle.lastIndexOf(hero.highlight))
: mobileTitle;
2026-05-18 09:43:29 +12:00
$: accessibleTitle = `${titleParts.lead}${titleParts.connector ? ` ${titleParts.connector}` : ''} ${hero.highlight}`.trim();
2026-05-15 01:28:10 +12:00
$: proofItems = (hero.subtitleChips ?? []).slice(0, 3);
2026-05-02 08:26:18 +12:00
2026-05-18 09:43:29 +12:00
const trustStars = Array.from({ length: 5 });
2026-05-02 08:26:18 +12:00
function splitTitle(title: string) {
const trimmed = title.trim();
if (trimmed.toLowerCase().endsWith(' in')) {
return {
lead: trimmed.slice(0, -3),
connector: 'in'
};
}
return {
lead: trimmed,
connector: ''
};
}
2026-05-07 21:47:42 +12:00
function linkTarget(external?: boolean) {
return external ? '_blank' : undefined;
}
function linkRel(external?: boolean) {
return external ? 'noopener' : undefined;
}
2026-05-02 08:26:18 +12:00
</script>
2026-05-26 23:30:22 +12:00
<section id="hero" data-track-location="hero">
2026-05-13 00:34:34 +12:00
<!-- hero-img is a direct child of #hero so it can be absolutely
positioned relative to the section on mobile without being
constrained by hero-inner's stacking context -->
<div class="hero-img">
<picture>
2026-05-18 09:43:29 +12:00
{#if hero.desktopImageWebpUrl}
<source media="(min-width: 769px)" srcset={hero.desktopImageWebpUrl} type="image/webp" />
{/if}
2026-05-13 00:34:34 +12:00
{#if hero.desktopImageUrl}
<source media="(min-width: 769px)" srcset={hero.desktopImageUrl} />
{/if}
2026-05-18 09:43:29 +12:00
{#if hero.imageWebpUrl}
<source srcset={hero.imageWebpUrl} type="image/webp" />
{/if}
2026-05-13 00:34:34 +12:00
<img
src={hero.imageUrl}
alt={hero.imageAlt}
2026-05-18 09:43:29 +12:00
width={hero.imageWidth ?? undefined}
height={hero.imageHeight ?? undefined}
2026-05-13 00:34:34 +12:00
loading="eager"
fetchpriority="high"
/>
</picture>
</div>
2026-05-02 08:26:18 +12:00
<div class="hero-inner">
<div class="hero-text">
2026-05-13 00:34:34 +12:00
{#if hero.kicker}
<p class="hero-kicker">{hero.kicker}</p>
{/if}
2026-05-02 08:26:18 +12:00
<h1 class="hero-heading">
2026-05-18 09:43:29 +12:00
<span class="visually-hidden">{accessibleTitle}</span>
<span class="hero-heading-desktop" aria-hidden="true">
2026-05-02 08:26:18 +12:00
<span class="hero-title-main">{titleParts.lead}</span>
{#if titleParts.connector}
<span class="hero-title-connector"> {titleParts.connector}</span>
{/if}
<br />
<span class="hero-title-highlight">{hero.highlight}</span>
</span>
2026-05-18 09:43:29 +12:00
<span class="hero-heading-mobile" aria-hidden="true">
2026-05-13 00:34:34 +12:00
{mobileLead}<span class="hero-title-highlight">{hero.highlight}</span>
</span>
2026-05-02 08:26:18 +12:00
</h1>
2026-05-18 09:43:29 +12:00
{#if hero.seoHeading}
<h2 class="hero-seo-heading">{hero.seoHeading}</h2>
{/if}
2026-05-03 11:49:59 +12:00
{#if hero.subtitle}
2026-05-13 00:34:34 +12:00
<p class="hero-subtitle hero-subtitle-desktop">{hero.subtitle}</p>
{/if}
2026-05-15 01:28:10 +12:00
{#if proofItems.length || reviewCta}
<div class="hero-chips" aria-label="Why owners choose Goodwalk">
{#each proofItems as chip}
2026-05-13 00:34:34 +12:00
<span class="hero-chip">
<Icon name={chip.icon} />
{chip.label}
</span>
{/each}
2026-05-15 01:28:10 +12:00
{#if reviewCta}
<a
class="hero-trust-chip"
href={reviewCta.href}
target={reviewCta.external ? '_blank' : undefined}
rel={reviewCta.external ? 'noopener' : undefined}
aria-label="Read our five-star Google reviews"
2026-05-26 23:30:22 +12:00
data-track-event="social_proof_click"
data-track-type="hero_reviews"
data-track-label={reviewCta.label}
2026-05-15 01:28:10 +12:00
>
2026-05-18 09:43:29 +12:00
<span class="hero-trust-mark" aria-hidden="true">
<img
class="hero-trust-logo"
src="/images/google-g-logo.svg"
alt=""
width="16"
height="17"
/>
</span>
<span class="hero-trust-stars" aria-hidden="true">
{#each trustStars as _, index}
<Icon name="fas fa-star" className={`hero-trust-star hero-trust-star-${index + 1}`} />
{/each}
</span>
<span class="hero-trust-label">{reviewCta.label}</span>
2026-05-15 01:28:10 +12:00
</a>
{/if}
2026-05-13 00:34:34 +12:00
</div>
2026-05-03 11:49:59 +12:00
{/if}
2026-05-02 08:26:18 +12:00
<div class="hero-buttons">
2026-05-07 21:47:42 +12:00
<a
href={primaryCta.href}
target={linkTarget(primaryCta.external)}
rel={linkRel(primaryCta.external)}
2026-05-18 09:43:29 +12:00
class="btn btn-yellow btn-with-arrow btn-hide-arrow-mobile"
on:click={handlePrimaryCtaClick}
2026-05-26 23:30:22 +12:00
data-track-event="cta_click"
data-track-type="hero_primary"
data-track-label={primaryCta.label}
2026-05-07 21:47:42 +12:00
>
{primaryCta.label}
2026-05-15 01:28:10 +12:00
<Icon name="fas fa-arrow-right" />
2026-05-07 21:47:42 +12:00
</a>
<a
href={hero.secondaryCta.href}
target={linkTarget(hero.secondaryCta.external)}
rel={linkRel(hero.secondaryCta.external)}
2026-05-15 01:28:10 +12:00
class="hero-secondary-link"
2026-05-26 23:30:22 +12:00
data-track-event="cta_click"
data-track-type="hero_secondary"
data-track-label={hero.secondaryCta.label}
2026-05-07 21:47:42 +12:00
>
{hero.secondaryCta.label}
2026-05-13 00:34:34 +12:00
<Icon name="fas fa-arrow-down" className="hero-cta-arrow" />
2026-05-07 21:47:42 +12:00
</a>
2026-05-02 08:26:18 +12:00
</div>
</div>
</div>
</section>