remove emojis with real icons
All checks were successful
Test and deploy staging / staging (push) Successful in 1m46s

This commit is contained in:
2026-09-23 16:12:41 +00:00
parent f1cd0ef849
commit 4b63e9ca11
12 changed files with 137 additions and 50 deletions

View File

@@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import Music2 from 'lucide-svelte/icons/music-2';
let { src, class: className = '' }: { src?: string; class?: string } = $props(); let { src, class: className = '' }: { src?: string; class?: string } = $props();
let failed = $state(false); let failed = $state(false);
$effect(() => { $effect(() => {
@@ -18,6 +19,6 @@
class="flex h-full items-center justify-center text-2xl text-gray-500" class="flex h-full items-center justify-center text-2xl text-gray-500"
aria-hidden="true" aria-hidden="true"
> >
♫ <Music2 size={28} />
</div>{/if} </div>{/if}
</div> </div>

View File

@@ -6,9 +6,6 @@ import postgres from 'postgres';
const client = postgres('postgresql://postgres:postgres@localhost:5432/db'); const client = postgres('postgresql://postgres:postgres@localhost:5432/db');
const db = drizzle(client, { schema }); const db = drizzle(client, { schema });
await db.insert(topics).values([ await db.insert(topics).values([{ name: 'Math' }, { name: 'Programming' }]);
{ name: 'Math', emoji: '👍' },
{ name: 'Programming', emoji: '👍' }
]);
process.exit(0); process.exit(0);

View File

@@ -6,6 +6,7 @@
import { page } from '$app/state'; import { page } from '$app/state';
import { getSeo } from '$lib/seo'; import { getSeo } from '$lib/seo';
import CopyButton from '$lib/components/CopyButton.svelte'; import CopyButton from '$lib/components/CopyButton.svelte';
import ExternalLink from 'lucide-svelte/icons/external-link';
import SiOsu from '~icons/simple-icons/osu'; import SiOsu from '~icons/simple-icons/osu';
import SiSteam from '~icons/simple-icons/steam'; import SiSteam from '~icons/simple-icons/steam';
import SiTwitch from '~icons/simple-icons/twitch'; import SiTwitch from '~icons/simple-icons/twitch';
@@ -257,7 +258,8 @@
<a <a
href="https://git.stanrunge.dev/stan/personal-website" href="https://git.stanrunge.dev/stan/personal-website"
rel="external" rel="external"
class="hover:text-[var(--site-text)]">source ↗</a class="inline-flex items-center gap-1 hover:text-[var(--site-text)]"
>source <ExternalLink size={14} aria-hidden="true" /></a
> >
</div> </div>
<nav class="flex flex-wrap items-center justify-center gap-1" aria-label="Social links"> <nav class="flex flex-wrap items-center justify-center gap-1" aria-label="Social links">

View File

@@ -2,6 +2,9 @@
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import portrait from '$lib/assets/stan.jpg?url'; import portrait from '$lib/assets/stan.jpg?url';
import ProgressBar from '$lib/components/ProgressBar.svelte'; import ProgressBar from '$lib/components/ProgressBar.svelte';
import ArrowRight from 'lucide-svelte/icons/arrow-right';
import ExternalLink from 'lucide-svelte/icons/external-link';
import Mail from 'lucide-svelte/icons/mail';
import type { PageProps } from './$types'; import type { PageProps } from './$types';
let { data }: PageProps = $props(); let { data }: PageProps = $props();
@@ -21,7 +24,7 @@
}, },
{ {
name: 'animaldex', name: 'animaldex',
subtitle: 'add neighborhood pets to the world map 😼', subtitle: 'add neighborhood pets to the world map',
href: '/projects/animaldex', href: '/projects/animaldex',
external: false external: false
}, },
@@ -68,15 +71,26 @@
<a <a
href="https://git.stanrunge.dev/stan" href="https://git.stanrunge.dev/stan"
rel="external" rel="external"
class="hover:text-[var(--site-text)]">git ↗</a class="inline-flex items-center gap-1 hover:text-[var(--site-text)]"
>git <ExternalLink size={14} aria-hidden="true" /></a
> >
<a href="https://x.com/stanrunge" rel="external" class="hover:text-[var(--site-text)]" <a
>twitter ↗</a href="https://x.com/stanrunge"
rel="external"
class="inline-flex items-center gap-1 hover:text-[var(--site-text)]"
>twitter <ExternalLink size={14} aria-hidden="true" /></a
> >
<a href="https://esports.vash.software" rel="external" class="hover:text-[var(--site-text)]" <a
>vash esports ↗</a href="https://esports.vash.software"
rel="external"
class="inline-flex items-center gap-1 hover:text-[var(--site-text)]"
>vash esports <ExternalLink size={14} aria-hidden="true" /></a
>
<a
href="mailto:stan@stanrunge.dev"
class="inline-flex items-center gap-1 hover:text-[var(--site-text)]"
>email <Mail size={14} aria-hidden="true" /></a
> >
<a href="mailto:stan@stanrunge.dev" class="hover:text-[var(--site-text)]">email ↗</a>
</nav> </nav>
</div> </div>
<img <img
@@ -93,7 +107,11 @@
<section aria-labelledby="projects-heading" class="border-t border-[var(--site-border)] pt-8"> <section aria-labelledby="projects-heading" class="border-t border-[var(--site-border)] pt-8">
<h2 id="projects-heading" class="mb-4 text-2xl font-semibold tracking-tight"> <h2 id="projects-heading" class="mb-4 text-2xl font-semibold tracking-tight">
<a href={resolve('/projects')} class="hover:text-[var(--site-muted)]">projects ↗</a> <a
href={resolve('/projects')}
class="inline-flex items-center gap-1 hover:text-[var(--site-muted)]"
>projects <ArrowRight size={20} aria-hidden="true" /></a
>
</h2> </h2>
<ul> <ul>
{#each projects as project (project.name)} {#each projects as project (project.name)}
@@ -108,7 +126,11 @@
<span class="block">{project.name}</span> <span class="block">{project.name}</span>
<span class="mt-1 block text-base text-[var(--site-muted)]">{project.subtitle}</span <span class="mt-1 block text-base text-[var(--site-muted)]">{project.subtitle}</span
> >
</span><span aria-hidden="true" class="shrink-0 text-[var(--site-muted)]">↗</span> </span><ExternalLink
size={18}
aria-hidden="true"
class="shrink-0 text-[var(--site-muted)]"
/>
</a> </a>
{:else} {:else}
<a <a
@@ -119,7 +141,11 @@
<span class="block">{project.name}</span> <span class="block">{project.name}</span>
<span class="mt-1 block text-base text-[var(--site-muted)]">{project.subtitle}</span <span class="mt-1 block text-base text-[var(--site-muted)]">{project.subtitle}</span
> >
</span><span aria-hidden="true" class="shrink-0 text-[var(--site-muted)]">↗</span> </span><ArrowRight
size={18}
aria-hidden="true"
class="shrink-0 text-[var(--site-muted)]"
/>
</a> </a>
{/if} {/if}
</li> </li>
@@ -129,7 +155,11 @@
<section aria-labelledby="lists-heading" class="pt-14 sm:pt-20"> <section aria-labelledby="lists-heading" class="pt-14 sm:pt-20">
<h2 id="lists-heading" class="mb-4 text-2xl font-semibold tracking-tight"> <h2 id="lists-heading" class="mb-4 text-2xl font-semibold tracking-tight">
<a href={resolve('/lists')} class="hover:text-[var(--site-muted)]">lists ↗</a> <a
href={resolve('/lists')}
class="inline-flex items-center gap-1 hover:text-[var(--site-muted)]"
>lists <ArrowRight size={20} aria-hidden="true" /></a
>
</h2> </h2>
<ul> <ul>
{#each data.lists as list (list.id)} {#each data.lists as list (list.id)}
@@ -140,12 +170,13 @@
> >
<span class="flex flex-wrap items-center justify-between gap-x-6 gap-y-1"> <span class="flex flex-wrap items-center justify-between gap-x-6 gap-y-1">
<span>{list.name}</span> <span>{list.name}</span>
<span class="text-base text-[var(--site-muted)]"> <span class="inline-flex items-center gap-1 text-base text-[var(--site-muted)]">
{#if list.isProgress && data.user} {#if list.isProgress && data.user}
{list.total ? Math.round((list.checked / list.total) * 100) : 0}% · {list.checked}/{list.total} {list.total ? Math.round((list.checked / list.total) * 100) : 0}% · {list.checked}/{list.total}
{:else} {:else}
{list.total} {list.total === 1 ? 'item' : 'items'} {list.total} {list.total === 1 ? 'item' : 'items'}
{/if} ↗ {/if}
<ArrowRight size={16} aria-hidden="true" />
</span> </span>
</span> </span>
{#if list.isProgress}<span class="mt-3 block" {#if list.isProgress}<span class="mt-3 block"

View File

@@ -1,6 +1,8 @@
<script lang="ts"> <script lang="ts">
import { enhance } from '$app/forms'; import { enhance } from '$app/forms';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import ArrowLeft from 'lucide-svelte/icons/arrow-left';
import ArrowRight from 'lucide-svelte/icons/arrow-right';
let { data, form } = $props(); let { data, form } = $props();
@@ -24,7 +26,8 @@
<div class="mx-auto max-w-5xl px-4 py-10 sm:px-6 sm:py-16"> <div class="mx-auto max-w-5xl px-4 py-10 sm:px-6 sm:py-16">
<a <a
href={resolve('/blog')} href={resolve('/blog')}
class="text-base text-[var(--site-muted)] hover:text-[var(--site-text)]">← blog</a class="inline-flex items-center gap-1 text-base text-[var(--site-muted)] hover:text-[var(--site-text)]"
><ArrowLeft size={16} aria-hidden="true" /> blog</a
> >
<div class="mt-5 mb-8 flex flex-wrap items-end justify-between gap-4"> <div class="mt-5 mb-8 flex flex-wrap items-end justify-between gap-4">
<div> <div>
@@ -34,7 +37,8 @@
{#if data.blogArticle.status === 'published'} {#if data.blogArticle.status === 'published'}
<a <a
href={resolve('/blog/[slug]', { slug: data.blogArticle.slug })} href={resolve('/blog/[slug]', { slug: data.blogArticle.slug })}
class="text-base underline underline-offset-4">view article ↗</a class="inline-flex items-center gap-1 text-base underline underline-offset-4"
>view article <ArrowRight size={16} aria-hidden="true" /></a
> >
{/if} {/if}
</div> </div>

View File

@@ -1,5 +1,7 @@
<script lang="ts"> <script lang="ts">
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import ArrowLeft from 'lucide-svelte/icons/arrow-left';
import Check from 'lucide-svelte/icons/check';
import { enhance } from '$app/forms'; import { enhance } from '$app/forms';
import ListPictureDropzone from '$lib/components/ListPictureDropzone.svelte'; import ListPictureDropzone from '$lib/components/ListPictureDropzone.svelte';
import ProgressBar from '$lib/components/ProgressBar.svelte'; import ProgressBar from '$lib/components/ProgressBar.svelte';
@@ -48,7 +50,8 @@
<section class="mx-auto max-w-4xl space-y-6 px-4 py-10 sm:px-6 sm:py-14"> <section class="mx-auto max-w-4xl space-y-6 px-4 py-10 sm:px-6 sm:py-14">
<a <a
href={resolve('/lists')} href={resolve('/lists')}
class="text-base text-[var(--site-muted)] hover:text-[var(--site-text)]">← lists</a class="inline-flex items-center gap-1 text-base text-[var(--site-muted)] hover:text-[var(--site-text)]"
><ArrowLeft size={16} aria-hidden="true" /> lists</a
> >
<div class="flex flex-wrap items-end justify-between gap-4"> <div class="flex flex-wrap items-end justify-between gap-4">
<div> <div>
@@ -173,7 +176,7 @@
class="flex h-8 w-8 items-center justify-center rounded-full border border-[var(--site-border)] text-base" class="flex h-8 w-8 items-center justify-center rounded-full border border-[var(--site-border)] text-base"
aria-label={`${completed.has(item.id) ? 'Uncheck' : 'Check'} ${item.name}`} aria-label={`${completed.has(item.id) ? 'Uncheck' : 'Check'} ${item.name}`}
> >
{completed.has(item.id) ? '✓' : ''} {#if completed.has(item.id)}<Check size={16} aria-hidden="true" />{/if}
</button> </button>
</form> </form>
{/if} {/if}

View File

@@ -1,5 +1,7 @@
<script lang="ts"> <script lang="ts">
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import ArrowRight from 'lucide-svelte/icons/arrow-right';
import ExternalLink from 'lucide-svelte/icons/external-link';
const projects = [ const projects = [
{ name: 'catchemall', href: '/projects/catch-em-all', external: false }, { name: 'catchemall', href: '/projects/catch-em-all', external: false },
@@ -30,18 +32,22 @@
rel="external" rel="external"
class="flex items-center justify-between gap-4 py-5 hover:text-[var(--site-muted)]" class="flex items-center justify-between gap-4 py-5 hover:text-[var(--site-muted)]"
> >
<span>{project.name}</span><span aria-hidden="true" class="text-[var(--site-muted)]" <span>{project.name}</span><ExternalLink
>↗</span size={18}
> aria-hidden="true"
class="text-[var(--site-muted)]"
/>
</a> </a>
{:else} {:else}
<a <a
href={resolve(project.href)} href={resolve(project.href)}
class="flex items-center justify-between gap-4 py-5 hover:text-[var(--site-muted)]" class="flex items-center justify-between gap-4 py-5 hover:text-[var(--site-muted)]"
> >
<span>{project.name}</span><span aria-hidden="true" class="text-[var(--site-muted)]" <span>{project.name}</span><ArrowRight
>↗</span size={18}
> aria-hidden="true"
class="text-[var(--site-muted)]"
/>
</a> </a>
{/if} {/if}
</li> </li>

View File

@@ -2,6 +2,9 @@
import { onMount, onDestroy, tick } from 'svelte'; import { onMount, onDestroy, tick } from 'svelte';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { page } from '$app/state'; import { page } from '$app/state';
import ArrowRight from 'lucide-svelte/icons/arrow-right';
import PawPrint from 'lucide-svelte/icons/paw-print';
import X from 'lucide-svelte/icons/x';
import type { Map as MaptilerMap, Marker as MaptilerMarker } from '@maptiler/sdk'; import type { Map as MaptilerMap, Marker as MaptilerMarker } from '@maptiler/sdk';
interface TrailPoint { interface TrailPoint {
@@ -223,7 +226,11 @@
el.style.backgroundSize = 'cover'; el.style.backgroundSize = 'cover';
el.style.backgroundPosition = 'center'; el.style.backgroundPosition = 'center';
} else { } else {
el.textContent = '🐾'; el.innerHTML =
'<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="4" r="2"/><circle cx="18" cy="8" r="2"/><circle cx="20" cy="16" r="2"/><path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z"/></svg>';
el.style.display = 'flex';
el.style.alignItems = 'center';
el.style.justifyContent = 'center';
} }
el.onclick = () => selectAnimal(feature); el.onclick = () => selectAnimal(feature);
@@ -409,7 +416,8 @@
<button <button
onclick={() => (selected = null)} onclick={() => (selected = null)}
aria-label="Close animal details" aria-label="Close animal details"
class="absolute top-3 right-3 rounded bg-black px-3 py-1 text-white">✕</button class="absolute top-3 right-3 flex h-8 w-8 items-center justify-center rounded bg-black text-white"
><X size={18} aria-hidden="true" /></button
> >
</div> </div>
{/if} {/if}
@@ -450,9 +458,9 @@
</div> </div>
<a <a
href={resolve(`/projects/animaldex/register?animalId=${selected.animalId}`)} href={resolve(`/projects/animaldex/register?animalId=${selected.animalId}`)}
class="mt-5 inline-block text-base underline underline-offset-4" class="mt-5 inline-flex items-center gap-1 text-base underline underline-offset-4"
> >
i saw this animal too ↗ i saw this animal too <ArrowRight size={16} aria-hidden="true" />
</a> </a>
</div> </div>
</div> </div>
@@ -478,7 +486,7 @@
class="h-12 w-12 rounded object-cover" class="h-12 w-12 rounded object-cover"
/>{:else}<span />{:else}<span
class="flex h-12 w-12 items-center justify-center rounded bg-[var(--site-surface)]" class="flex h-12 w-12 items-center justify-center rounded bg-[var(--site-surface)]"
>🐾</span ><PawPrint size={24} aria-hidden="true" /></span
>{/if} >{/if}
<span class="min-w-0"> <span class="min-w-0">
<span class="block font-medium">{animal.animalName || animal.species}</span> <span class="block font-medium">{animal.animalName || animal.species}</span>

View File

@@ -2,6 +2,12 @@
import type { PageData } from './$types'; import type { PageData } from './$types';
import { invalidateAll } from '$app/navigation'; import { invalidateAll } from '$app/navigation';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import ArrowLeft from 'lucide-svelte/icons/arrow-left';
import Bot from 'lucide-svelte/icons/bot';
import Check from 'lucide-svelte/icons/check';
import MapPin from 'lucide-svelte/icons/map-pin';
import PawPrint from 'lucide-svelte/icons/paw-print';
import X from 'lucide-svelte/icons/x';
let { data }: { data: PageData } = $props(); let { data }: { data: PageData } = $props();
@@ -40,8 +46,10 @@
</div> </div>
{:else} {:else}
<section class="mx-auto max-w-4xl px-4 py-10 sm:px-6 sm:py-14"> <section class="mx-auto max-w-4xl px-4 py-10 sm:px-6 sm:py-14">
<a href={resolve('/projects/animaldex')} class="text-base text-[var(--site-muted)]" <a
>← animaldex</a href={resolve('/projects/animaldex')}
class="inline-flex items-center gap-1 text-base text-[var(--site-muted)]"
><ArrowLeft size={16} aria-hidden="true" /> animaldex</a
> >
<div class="mt-5 mb-8 flex flex-wrap items-end justify-between gap-4"> <div class="mt-5 mb-8 flex flex-wrap items-end justify-between gap-4">
<h1 class="text-4xl font-semibold tracking-tight">animaldex admin</h1> <h1 class="text-4xl font-semibold tracking-tight">animaldex admin</h1>
@@ -65,7 +73,9 @@
{#if item.photoUrl} {#if item.photoUrl}
<img src={item.photoUrl} alt="" class="h-full w-full object-cover" /> <img src={item.photoUrl} alt="" class="h-full w-full object-cover" />
{:else} {:else}
<div class="flex h-full items-center justify-center text-2xl">🐾</div> <div class="flex h-full items-center justify-center">
<PawPrint size={28} aria-hidden="true" />
</div>
{/if} {/if}
</div> </div>
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
@@ -81,8 +91,8 @@
</span> </span>
</div> </div>
{#if item.aiBreedSuggestion} {#if item.aiBreedSuggestion}
<p class="mt-1 text-base text-[var(--site-muted)]"> <p class="mt-1 flex items-center gap-1 text-base text-[var(--site-muted)]">
🤖 AI: {item.aiBreedSuggestion} ({Math.round( <Bot size={16} aria-hidden="true" /> AI: {item.aiBreedSuggestion} ({Math.round(
(item.aiBreedConfidence ?? 0) * 100 (item.aiBreedConfidence ?? 0) * 100
)}%) )}%)
</p> </p>
@@ -97,8 +107,9 @@
})} })}
{#if item.reporterName}· by {item.reporterName}{/if} {#if item.reporterName}· by {item.reporterName}{/if}
</p> </p>
<p class="mt-0.5 text-base text-[var(--site-muted)]"> <p class="mt-0.5 flex items-center gap-1 text-base text-[var(--site-muted)]">
📍 {item.sightingLat.toFixed(5)}, {item.sightingLng.toFixed(5)} <MapPin size={16} aria-hidden="true" />
{item.sightingLat.toFixed(5)}, {item.sightingLng.toFixed(5)}
</p> </p>
</div> </div>
</div> </div>
@@ -106,16 +117,22 @@
<button <button
onclick={() => moderate(item.animalId, 'accept')} onclick={() => moderate(item.animalId, 'accept')}
disabled={actionLoading !== null || status === 'accepted'} disabled={actionLoading !== null || status === 'accepted'}
class="flex-1 rounded border border-[var(--site-border)] py-2 text-base disabled:opacity-50" class="inline-flex flex-1 items-center justify-center gap-1 rounded border border-[var(--site-border)] py-2 text-base disabled:opacity-50"
> >
{actionLoading === `animals-${item.animalId}-accept` ? '…' : '✓ Accept'} {#if actionLoading === `animals-${item.animalId}-accept`}…{:else}<Check
size={16}
aria-hidden="true"
/> Accept{/if}
</button> </button>
<button <button
onclick={() => moderate(item.animalId, 'deny')} onclick={() => moderate(item.animalId, 'deny')}
disabled={actionLoading !== null || status === 'denied'} disabled={actionLoading !== null || status === 'denied'}
class="flex-1 rounded border border-[var(--site-border)] py-2 text-base disabled:opacity-50" class="inline-flex flex-1 items-center justify-center gap-1 rounded border border-[var(--site-border)] py-2 text-base disabled:opacity-50"
> >
{actionLoading === `animals-${item.animalId}-deny` ? '…' : '✕ Deny'} {#if actionLoading === `animals-${item.animalId}-deny`}…{:else}<X
size={16}
aria-hidden="true"
/> Deny{/if}
</button> </button>
</div> </div>
</div> </div>

View File

@@ -4,6 +4,8 @@
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import ArrowLeft from 'lucide-svelte/icons/arrow-left';
import X from 'lucide-svelte/icons/x';
const animalId = $derived($page.url.searchParams.get('animalId')); const animalId = $derived($page.url.searchParams.get('animalId'));
const isNewSighting = $derived(!!animalId); const isNewSighting = $derived(!!animalId);
@@ -240,7 +242,8 @@
<section class="mx-auto max-w-3xl px-4 py-10 sm:px-6 sm:py-14"> <section class="mx-auto max-w-3xl px-4 py-10 sm:px-6 sm:py-14">
<a <a
href={resolve('/projects/animaldex')} href={resolve('/projects/animaldex')}
class="text-base text-[var(--site-muted)] hover:text-[var(--site-text)]">← animaldex</a class="inline-flex items-center gap-1 text-base text-[var(--site-muted)] hover:text-[var(--site-text)]"
><ArrowLeft size={16} aria-hidden="true" /> animaldex</a
> >
<h1 class="mt-5 text-4xl font-semibold tracking-tight sm:text-5xl"> <h1 class="mt-5 text-4xl font-semibold tracking-tight sm:text-5xl">
{isNewSighting ? 'add a sighting' : 'add an animal'} {isNewSighting ? 'add a sighting' : 'add an animal'}
@@ -278,7 +281,7 @@
onclick={() => removePhoto(i)} onclick={() => removePhoto(i)}
aria-label={`Remove photo ${i + 1}`} aria-label={`Remove photo ${i + 1}`}
class="absolute top-1 right-1 flex h-5 w-5 items-center justify-center rounded-full bg-black/60 text-xs text-white" class="absolute top-1 right-1 flex h-5 w-5 items-center justify-center rounded-full bg-black/60 text-xs text-white"
>✕</button ><X size={12} aria-hidden="true" /></button
> >
</div> </div>
{/each} {/each}

View File

@@ -1,5 +1,7 @@
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import Check from 'lucide-svelte/icons/check';
import ExternalLink from 'lucide-svelte/icons/external-link';
import { horses, race, type Horse } from '$lib/derby/data'; import { horses, race, type Horse } from '$lib/derby/data';
import { scenarios } from '$lib/derby/practice'; import { scenarios } from '$lib/derby/practice';
import { races } from '$lib/derby/races'; import { races } from '$lib/derby/races';
@@ -497,7 +499,9 @@
<ol class="steps"> <ol class="steps">
{#each steps as st, i (st.title)} {#each steps as st, i (st.title)}
<li class:done={st.done}> <li class:done={st.done}>
<span class="step-n">{st.done ? '✓' : i + 1}</span> <span class="step-n"
>{#if st.done}<Check size={16} aria-hidden="true" />{:else}{i + 1}{/if}</span
>
<div> <div>
<a href={st.href}>{st.title}</a> <a href={st.href}>{st.title}</a>
<p>{st.detail}</p> <p>{st.detail}</p>
@@ -969,7 +973,9 @@
</ul> </ul>
<div class="source-links"> <div class="source-links">
{#each race.sources as s (s.href)} {#each race.sources as s (s.href)}
<a href={s.href} target="_blank" rel="noreferrer">{s.label} ↗</a> <a href={s.href} target="_blank" rel="noreferrer"
>{s.label} <ExternalLink size={14} aria-hidden="true" /></a
>
{/each} {/each}
</div> </div>
</div> </div>
@@ -1915,6 +1921,9 @@
margin-top: 16px; margin-top: 16px;
} }
.source-links a { .source-links a {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.76rem; font-size: 0.76rem;
border: 1px solid #07100c; border: 1px solid #07100c;
border-radius: 99px; border-radius: 99px;

View File

@@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { untrack } from 'svelte'; import { untrack } from 'svelte';
import ExternalLink from 'lucide-svelte/icons/external-link';
import { races, type Runner } from '$lib/derby/races'; import { races, type Runner } from '$lib/derby/races';
import { betValue, fairOdds, formatTime, placings } from '$lib/derby/model'; import { betValue, fairOdds, formatTime, placings } from '$lib/derby/model';
import { import {
@@ -708,7 +709,9 @@
</ul> </ul>
<div class="source-links"> <div class="source-links">
{#each race.sources as s (s.href)} {#each race.sources as s (s.href)}
<a href={s.href} target="_blank" rel="noreferrer">{s.label} ↗</a> <a href={s.href} target="_blank" rel="noreferrer"
>{s.label} <ExternalLink size={14} aria-hidden="true" /></a
>
{/each} {/each}
</div> </div>
</div> </div>
@@ -1518,6 +1521,9 @@
margin-top: 16px; margin-top: 16px;
} }
.source-links a { .source-links a {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.76rem; font-size: 0.76rem;
border: 1px solid #07100c; border: 1px solid #07100c;
border-radius: 99px; border-radius: 99px;