strip more seamless and margin improvements

This commit is contained in:
Vincent van der Wal
2026-08-01 11:38:02 +02:00
parent 99c1a89537
commit a0379a1fd6
13 changed files with 706 additions and 188 deletions
+118
View File
@@ -0,0 +1,118 @@
<script lang="ts">
import { resolve } from '$app/paths';
// A hand-picked set of the prerendered city pages (see
// routes/weather/locations/city-names100.json) — instant navigation targets
// that double as SEO entry points.
const popularCities: { slug: string; label: string }[] = [
{ slug: 'london', label: 'London' },
{ slug: 'tokyo', label: 'Tokyo' },
{ slug: 'berlin', label: 'Berlin' },
{ slug: 'sydney', label: 'Sydney' },
{ slug: 'singapore', label: 'Singapore' },
{ slug: 'dubai', label: 'Dubai' },
{ slug: 'los-angeles', label: 'Los Angeles' },
{ slug: 'hong-kong', label: 'Hong Kong' },
{ slug: 'istanbul', label: 'Istanbul' },
{ slug: 'seoul', label: 'Seoul' },
{ slug: 'mexico-city', label: 'Mexico City' },
{ slug: 'sao-paulo', label: 'São Paulo' }
];
const forecastLinks = [
{ href: resolve('/weather/week'), label: '7-Day Forecast' },
{ href: resolve('/weather/compare'), label: 'Model Comparison' },
{ href: resolve('/weather/14-day'), label: '14-Day Forecast' },
{ href: resolve('/weather/historical'), label: 'Historical Weather' },
{ href: resolve('/weather/maps'), label: 'Weather Maps' }
];
const year = new Date().getFullYear();
</script>
<footer class="mt-16 border-t border-border bg-card/60">
<div class="mx-auto w-full max-w-[1536px] px-4 py-10 lg:px-8">
<div class="grid gap-10 sm:grid-cols-2 lg:grid-cols-4">
<!-- Brand -->
<div class="max-w-xs">
<div class="flex items-center gap-2">
<span class="flex h-8 w-8 items-center justify-center rounded-lg bg-primary text-lg"
></span
>
<span class="text-lg font-bold tracking-tight">Drizz.li</span>
</div>
<p class="mt-3 text-sm leading-relaxed text-muted-foreground">
Fast, no-nonsense weather forecasts built on open data.
</p>
<p class="mt-3 text-xs text-muted-foreground">
Weather data by
<a
class="font-medium underline-offset-2 hover:text-foreground hover:underline"
href="https://open-meteo.com"
target="_blank"
rel="noopener noreferrer">Open-Meteo</a
>
</p>
</div>
<!-- Forecasts -->
<nav aria-label="Forecasts">
<h3 class="text-[11px] font-bold tracking-wider text-muted-foreground uppercase">
Forecasts
</h3>
<ul class="mt-3 flex flex-col gap-2 text-sm">
{#each forecastLinks as link (link.href)}
<li>
<a
class="text-foreground/80 underline-offset-2 hover:text-foreground hover:underline"
href={link.href}>{link.label}</a
>
</li>
{/each}
</ul>
</nav>
<!-- Popular locations (spans two columns of links) -->
<nav aria-label="Popular locations" class="lg:col-span-2">
<h3 class="text-[11px] font-bold tracking-wider text-muted-foreground uppercase">
Popular locations
</h3>
<ul class="mt-3 grid grid-cols-2 gap-x-6 gap-y-2 text-sm sm:grid-cols-3">
{#each popularCities as city (city.slug)}
<li>
<a
class="text-foreground/80 underline-offset-2 hover:text-foreground hover:underline"
href={resolve('/weather/week/[location]', { location: city.slug })}
>{city.label} weather</a
>
</li>
{/each}
</ul>
</nav>
</div>
<!-- Bottom bar: legal -->
<div
class="mt-10 flex flex-col items-start justify-between gap-3 border-t border-border/70 pt-5 text-xs text-muted-foreground sm:flex-row sm:items-center"
>
<span>© {year} Drizz.li</span>
<nav aria-label="Legal" class="flex flex-wrap items-center gap-x-5 gap-y-1">
<a class="underline-offset-2 hover:text-foreground hover:underline" href={resolve('/about')}
>About</a
>
<a
class="underline-offset-2 hover:text-foreground hover:underline"
href={resolve('/legal/imprint')}>Imprint</a
>
<a
class="underline-offset-2 hover:text-foreground hover:underline"
href={resolve('/legal/privacy')}>Privacy</a
>
<a
class="underline-offset-2 hover:text-foreground hover:underline"
href={resolve('/legal/terms')}>Terms</a
>
</nav>
</div>
</div>
</footer>
@@ -26,6 +26,16 @@
location = value;
});
// Prerendered pages bake the DEFAULT location's flag into the HTML, and
// Svelte's hydration repairs text but not attributes — so on pages that
// never update the store (legal pages etc.) the stale flag would stick
// around next to the correct location name. Re-sync the src after mount.
let flagEl = $state<HTMLImageElement>();
$effect(() => {
const src = `/images/country-flags/${(location.country_code || 'united_nations').toLowerCase()}.svg`;
if (flagEl && !flagEl.src.endsWith(src)) flagEl.src = src;
});
const themeCycle: Theme[] = ['system', 'light', 'dark'];
const themeTitles: Record<Theme, string> = {
system: 'Theme: follow system',
@@ -76,6 +86,7 @@
class="hidden items-center gap-2 rounded-full border border-border/70 bg-muted/40 py-1 ps-1 pe-3 lg:flex"
>
<img
bind:this={flagEl}
class="h-6 w-6 shrink-0 rounded-full ring-1 ring-border"
src="/images/country-flags/{(location.country_code || 'united_nations').toLowerCase()}.svg"
alt={location.country}
@@ -131,35 +131,7 @@
{/each}
</nav>
<!-- About / legal links (hidden when collapsed; the pages stay reachable
via any expanded sidebar and the signup page footer) -->
{#if !collapsed}
<div class="px-4 py-3 text-[11px] leading-relaxed text-sidebar-foreground/60">
<a
class="hover:text-sidebar-foreground hover:underline"
href={resolve('/about')}
onclick={onMobileClose}>About</a
>
<span aria-hidden="true"> · </span>
<a
class="hover:text-sidebar-foreground hover:underline"
href={resolve('/legal/imprint')}
onclick={onMobileClose}>Imprint</a
>
<span aria-hidden="true"> · </span>
<a
class="hover:text-sidebar-foreground hover:underline"
href={resolve('/legal/privacy')}
onclick={onMobileClose}>Privacy</a
>
<span aria-hidden="true"> · </span>
<a
class="hover:text-sidebar-foreground hover:underline"
href={resolve('/legal/terms')}
onclick={onMobileClose}>Terms</a
>
</div>
{/if}
<!-- About / legal links moved to the page footer -->
<!-- Collapse toggle (desktop sidebar only; the mobile drawer omits onToggle) -->
{#if onToggle}