Design language

This commit is contained in:
2026-05-13 00:34:34 +12:00
parent ac6179e776
commit 6c943b14bd
34 changed files with 1986 additions and 697 deletions
+35 -1
View File
@@ -10,6 +10,12 @@
import '@fontsource/readex-pro/latin-500.css';
import '@fontsource/readex-pro/latin-600.css';
import '@fontsource/readex-pro/latin-700.css';
import '@fontsource/roboto/latin-400.css';
import '@fontsource/roboto/latin-500.css';
import '@fontsource/roboto/latin-700.css';
import '@fontsource/noto-sans/latin-400.css';
import '@fontsource/noto-sans/latin-500.css';
import '@fontsource/noto-sans/latin-700.css';
import '@fontsource/unbounded/latin-400.css';
import '@fontsource/unbounded/latin-600.css';
import '@fontsource/unbounded/latin-700.css';
@@ -57,9 +63,30 @@
targets.forEach(el => revealObserver!.observe(el));
}
function bounceSection(hash: string) {
const id = hash.startsWith('#') ? hash.slice(1) : hash;
if (!id) return;
// Wait for smooth scroll to finish before playing the bounce
setTimeout(() => {
const el = document.getElementById(id);
if (!el) return;
el.classList.remove('anchor-bounce');
void el.offsetWidth; // force reflow so re-adding the class re-triggers
el.classList.add('anchor-bounce');
el.addEventListener('animationend', () => el.classList.remove('anchor-bounce'), { once: true });
}, 520);
}
onMount(() => {
initClickTracking();
requestAnimationFrame(initReveal);
// Same-page hash clicks aren't caught by afterNavigate
function onHashChange() {
bounceSection(window.location.hash);
}
window.addEventListener('hashchange', onHashChange);
return () => window.removeEventListener('hashchange', onHashChange);
});
function shouldShowSkeleton() {
@@ -83,7 +110,14 @@
$: showRouteSkeleton = shouldShowSkeleton();
afterNavigate(({ from, to }) => {
if (!from || !to || to.url.hash) {
if (!from || !to) return;
if (to.url.hash && from.url.pathname !== to.url.pathname) {
bounceSection(to.url.hash);
return;
}
if (to.url.hash) {
return;
}
-1
View File
@@ -85,7 +85,6 @@
reviewCount: '30'
},
review: data.content.testimonials.map((testimonial) => ({
'@context': 'https://schema.org',
'@type': 'Review',
reviewRating: {
'@type': 'Rating',
+6 -52
View File
@@ -5,6 +5,7 @@
import Header from '$lib/components/Header.svelte';
import BookingPage from '$lib/components/BookingPage.svelte';
import LegalPage from '$lib/components/LegalPage.svelte';
import PageHeader from '$lib/components/PageHeader.svelte';
import PricingPage from '$lib/components/PricingPage.svelte';
import { aboutPageContent } from '$lib/content/about';
import ServiceLandingPage from '$lib/components/ServiceLandingPage.svelte';
@@ -81,11 +82,7 @@
name: 'Goodwalk Pack Walks',
description: data.page.description,
serviceType: 'Pack walks for small and medium dogs',
provider: {
'@type': 'LocalBusiness',
name: 'Goodwalk',
url: siteUrl
},
provider: { '@id': 'https://www.goodwalk.co.nz/#business' },
areaServed,
image: absoluteUrl(seoImage),
url: `${siteUrl}${data.page.canonicalPath}`,
@@ -107,11 +104,7 @@
name: 'Goodwalk 1:1 Dog Walks',
description: data.page.description,
serviceType: 'One-on-one dog walking',
provider: {
'@type': 'LocalBusiness',
name: 'Goodwalk',
url: siteUrl
},
provider: { '@id': 'https://www.goodwalk.co.nz/#business' },
areaServed,
image: absoluteUrl(seoImage),
url: `${siteUrl}${data.page.canonicalPath}`,
@@ -133,11 +126,7 @@
name: 'Goodwalk Puppy Visits',
description: data.page.description,
serviceType: 'In-home puppy visits',
provider: {
'@type': 'LocalBusiness',
name: 'Goodwalk',
url: siteUrl
},
provider: { '@id': 'https://www.goodwalk.co.nz/#business' },
areaServed,
image: absoluteUrl(seoImage),
url: `${siteUrl}${data.page.canonicalPath}`,
@@ -202,6 +191,7 @@
imageAlt={seoImageAlt}
structuredData={pageStructuredData}
preloadImage={preloadHeroImage}
noindex={data.page.noindex ?? false}
/>
<Header navigation={data.content.navigation} />
@@ -228,11 +218,7 @@
/>
{:else}
<main class="static-page">
<section class="static-page-hero">
<div class="static-page-inner">
<h1>{data.page.title}</h1>
</div>
</section>
<PageHeader variant="white" title={data.page.title} />
</main>
{/if}
@@ -243,36 +229,4 @@
min-height: 50vh;
background: var(--off-white);
}
.static-page-hero {
padding: 96px 0 120px;
}
.static-page-inner {
max-width: var(--max-w);
margin: 0 auto;
padding: 0 50px;
}
h1 {
font-family: var(--font-head);
font-size: 56px;
line-height: 1.05;
letter-spacing: -0.04em;
color: #000;
}
@media (max-width: 768px) {
.static-page-hero {
padding: 56px 0 72px;
}
.static-page-inner {
padding: 0 24px;
}
h1 {
font-size: 34px;
}
}
</style>
+15
View File
@@ -4,6 +4,21 @@ export const GET: RequestHandler = () => {
const body = [
'User-agent: *',
'Allow: /',
'Disallow: /api/',
'Disallow: /contract',
'',
'# AI crawlers — explicitly permitted',
'User-agent: GPTBot',
'Allow: /',
'',
'User-agent: OAI-SearchBot',
'Allow: /',
'',
'User-agent: ClaudeBot',
'Allow: /',
'',
'User-agent: PerplexityBot',
'Allow: /',
'',
'Sitemap: https://www.goodwalk.co.nz/sitemap.xml'
].join('\n');
+14 -11
View File
@@ -7,24 +7,26 @@ interface SitemapRoute {
path: string;
priority: string;
changefreq: string;
lastmod: string;
}
const routes: SitemapRoute[] = [
{ path: '/', priority: '1.0', changefreq: 'weekly' },
{ path: '/pack-walks', priority: '0.9', changefreq: 'monthly' },
{ path: '/dog-walking', priority: '0.9', changefreq: 'monthly' },
{ path: '/puppy-visits', priority: '0.9', changefreq: 'monthly' },
{ path: '/our-pricing', priority: '0.8', changefreq: 'monthly' },
{ path: '/about', priority: '0.7', changefreq: 'monthly' },
{ path: '/contact-us', priority: '0.7', changefreq: 'monthly' },
{ path: '/terms-and-conditions', priority: '0.3', changefreq: 'yearly' },
{ path: '/privacy-policy', priority: '0.3', changefreq: 'yearly' }
{ path: '/', priority: '1.0', changefreq: 'weekly', lastmod: '2026-05-12' },
{ path: '/pack-walks', priority: '0.9', changefreq: 'monthly', lastmod: '2026-05-12' },
{ path: '/dog-walking', priority: '0.9', changefreq: 'monthly', lastmod: '2026-05-12' },
{ path: '/puppy-visits', priority: '0.9', changefreq: 'monthly', lastmod: '2026-05-12' },
{ path: '/our-pricing', priority: '0.8', changefreq: 'monthly', lastmod: '2026-05-12' },
{ path: '/about', priority: '0.7', changefreq: 'monthly', lastmod: '2026-05-12' },
{ path: '/contact-us', priority: '0.7', changefreq: 'monthly', lastmod: '2026-05-12' },
{ path: '/terms-and-conditions', priority: '0.3', changefreq: 'yearly', lastmod: '2026-05-12' },
{ path: '/privacy-policy', priority: '0.3', changefreq: 'yearly', lastmod: '2026-05-12' }
];
const locationRoutes: SitemapRoute[] = locationPages.map((loc) => ({
path: `/locations/${loc.slug}`,
priority: '0.8',
changefreq: 'monthly'
changefreq: 'monthly',
lastmod: '2026-05-12'
}));
export const GET: RequestHandler = () => {
@@ -33,8 +35,9 @@ export const GET: RequestHandler = () => {
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${allRoutes
.map(
({ path, priority, changefreq }) => ` <url>
({ path, priority, changefreq, lastmod }) => ` <url>
<loc>${siteUrl}${path}</loc>
<lastmod>${lastmod}</lastmod>
<changefreq>${changefreq}</changefreq>
<priority>${priority}</priority>
</url>`
+6 -11
View File
@@ -1,15 +1,8 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { describe, expect, it } from 'vitest';
import { GET } from './+server';
describe('sitemap endpoint', () => {
afterEach(() => {
vi.useRealTimers();
});
it('returns a sitemap covering the published routes', async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-05-01T09:15:00Z'));
const response = await GET({} as never);
const body = await response.text();
@@ -17,8 +10,10 @@ describe('sitemap endpoint', () => {
expect(body).toContain('<loc>https://www.goodwalk.co.nz/</loc>');
expect(body).toContain('<loc>https://www.goodwalk.co.nz/contact-us</loc>');
expect(body).toContain('<loc>https://www.goodwalk.co.nz/privacy-policy</loc>');
expect(body).toContain('<lastmod>2026-05-01</lastmod>');
expect(body).not.toContain('/locations/');
expect(body.match(/<url>/g)).toHaveLength(9);
expect(body).toContain('<lastmod>2026-05-12</lastmod>');
expect(body).toContain('<loc>https://www.goodwalk.co.nz/locations/mt-eden</loc>');
expect(body).toContain('<loc>https://www.goodwalk.co.nz/locations/kingsland</loc>');
// 9 core pages + 17 location pages
expect(body.match(/<url>/g)).toHaveLength(26);
});
});