add projects dropdown
All checks were successful
Test and deploy staging / staging (push) Successful in 1m48s
All checks were successful
Test and deploy staging / staging (push) Successful in 1m48s
This commit is contained in:
38
src/lib/projects.ts
Normal file
38
src/lib/projects.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
export const projects = [
|
||||||
|
{
|
||||||
|
name: 'vash esports',
|
||||||
|
subtitle: 'the best way to play osu!',
|
||||||
|
href: 'https://esports.vash.software',
|
||||||
|
external: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'catchemall',
|
||||||
|
subtitle: 'catch 1025 pokemon across 53 games',
|
||||||
|
href: '/projects/catch-em-all',
|
||||||
|
external: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'animaldex',
|
||||||
|
subtitle: 'add neighborhood pets to the world map',
|
||||||
|
href: '/projects/animaldex',
|
||||||
|
external: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'osu! map finder',
|
||||||
|
subtitle: 'find osu! maps for Spotify songs/albums/playlists',
|
||||||
|
href: '/projects/spotify-beatmap-finder',
|
||||||
|
external: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'derby lens',
|
||||||
|
subtitle: 'estimate odds for horse betting',
|
||||||
|
href: '/projects/derby',
|
||||||
|
external: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'money tracker',
|
||||||
|
subtitle: 'a manual ledger for accounts, bills and utilities',
|
||||||
|
href: '/projects/money-tracker',
|
||||||
|
external: false
|
||||||
|
}
|
||||||
|
] as const;
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
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 { projects } from '$lib/projects';
|
||||||
|
import ChevronDown from 'lucide-svelte/icons/chevron-down';
|
||||||
import ExternalLink from 'lucide-svelte/icons/external-link';
|
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';
|
||||||
@@ -171,13 +173,73 @@
|
|||||||
</a>
|
</a>
|
||||||
<nav class="ml-auto hidden items-center gap-6 text-base md:flex" aria-label="Main navigation">
|
<nav class="ml-auto hidden items-center gap-6 text-base md:flex" aria-label="Main navigation">
|
||||||
{#each navigation as item (item.href)}
|
{#each navigation as item (item.href)}
|
||||||
<a
|
{#if item.href === '/projects'}
|
||||||
href={resolve(item.href)}
|
<div class="group relative">
|
||||||
aria-current={current(item.href) ? 'page' : undefined}
|
<a
|
||||||
class={current(item.href)
|
href={resolve(item.href)}
|
||||||
? 'text-[var(--site-text)]'
|
aria-current={current(item.href) ? 'page' : undefined}
|
||||||
: 'text-[var(--site-muted)] hover:text-[var(--site-text)]'}>{item.label}</a
|
aria-haspopup="true"
|
||||||
>
|
class="inline-flex items-center gap-1 {current(item.href)
|
||||||
|
? 'text-[var(--site-text)]'
|
||||||
|
: 'text-[var(--site-muted)] hover:text-[var(--site-text)]'}"
|
||||||
|
>{item.label}
|
||||||
|
<ChevronDown
|
||||||
|
size={14}
|
||||||
|
aria-hidden="true"
|
||||||
|
class="transition-transform group-focus-within:rotate-180 group-hover:rotate-180"
|
||||||
|
/></a
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="invisible absolute top-full left-1/2 z-50 -translate-x-1/2 pt-3 opacity-0 transition-opacity group-focus-within:visible group-focus-within:opacity-100 group-hover:visible group-hover:opacity-100"
|
||||||
|
>
|
||||||
|
<ul
|
||||||
|
class="w-72 rounded-md border border-[var(--site-border)] bg-[var(--site-bg)] py-2 shadow-lg"
|
||||||
|
>
|
||||||
|
{#each projects as project (project.name)}
|
||||||
|
<li>
|
||||||
|
{#if project.external}
|
||||||
|
<a
|
||||||
|
href={project.href}
|
||||||
|
rel="external"
|
||||||
|
class="flex items-center justify-between gap-3 px-4 py-2 hover:bg-[var(--site-surface)] focus-visible:bg-[var(--site-surface)]"
|
||||||
|
>
|
||||||
|
<span class="min-w-0">
|
||||||
|
<span class="block text-[var(--site-text)]">{project.name}</span>
|
||||||
|
<span class="block truncate text-sm text-[var(--site-muted)]"
|
||||||
|
>{project.subtitle}</span
|
||||||
|
>
|
||||||
|
</span><ExternalLink
|
||||||
|
size={14}
|
||||||
|
aria-hidden="true"
|
||||||
|
class="shrink-0 text-[var(--site-muted)]"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
{:else}
|
||||||
|
<a
|
||||||
|
href={resolve(project.href)}
|
||||||
|
aria-current={current(project.href) ? 'page' : undefined}
|
||||||
|
class="block px-4 py-2 hover:bg-[var(--site-surface)] focus-visible:bg-[var(--site-surface)]"
|
||||||
|
>
|
||||||
|
<span class="block text-[var(--site-text)]">{project.name}</span>
|
||||||
|
<span class="block truncate text-sm text-[var(--site-muted)]"
|
||||||
|
>{project.subtitle}</span
|
||||||
|
>
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<a
|
||||||
|
href={resolve(item.href)}
|
||||||
|
aria-current={current(item.href) ? 'page' : undefined}
|
||||||
|
class={current(item.href)
|
||||||
|
? 'text-[var(--site-text)]'
|
||||||
|
: 'text-[var(--site-muted)] hover:text-[var(--site-text)]'}>{item.label}</a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</nav>
|
</nav>
|
||||||
<div class="ml-auto hidden items-center gap-5 text-base md:ml-4 md:flex">
|
<div class="ml-auto hidden items-center gap-5 text-base md:ml-4 md:flex">
|
||||||
@@ -222,6 +284,29 @@
|
|||||||
<a href={resolve(item.href)} onclick={() => (menuOpen = false)} class="w-full py-2"
|
<a href={resolve(item.href)} onclick={() => (menuOpen = false)} class="w-full py-2"
|
||||||
>{item.label}</a
|
>{item.label}</a
|
||||||
>
|
>
|
||||||
|
{#if item.href === '/projects'}
|
||||||
|
<ul class="mb-2 flex w-full flex-col text-[var(--site-muted)]">
|
||||||
|
{#each projects as project (project.name)}
|
||||||
|
<li>
|
||||||
|
{#if project.external}
|
||||||
|
<a
|
||||||
|
href={project.href}
|
||||||
|
rel="external"
|
||||||
|
onclick={() => (menuOpen = false)}
|
||||||
|
class="inline-flex items-center gap-1 py-1.5 hover:text-[var(--site-text)]"
|
||||||
|
>{project.name} <ExternalLink size={12} aria-hidden="true" /></a
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<a
|
||||||
|
href={resolve(project.href)}
|
||||||
|
onclick={() => (menuOpen = false)}
|
||||||
|
class="block py-1.5 hover:text-[var(--site-text)]">{project.name}</a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
<div
|
<div
|
||||||
class="mt-3 flex w-full items-center justify-center gap-5 border-t border-[var(--site-border)] pt-4"
|
class="mt-3 flex w-full items-center justify-center gap-5 border-t border-[var(--site-border)] pt-4"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
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 { projects } from '$lib/projects';
|
||||||
import ArrowRight from 'lucide-svelte/icons/arrow-right';
|
import ArrowRight from 'lucide-svelte/icons/arrow-right';
|
||||||
import ExternalLink from 'lucide-svelte/icons/external-link';
|
import ExternalLink from 'lucide-svelte/icons/external-link';
|
||||||
import Mail from 'lucide-svelte/icons/mail';
|
import Mail from 'lucide-svelte/icons/mail';
|
||||||
@@ -9,38 +10,6 @@
|
|||||||
|
|
||||||
let { data }: PageProps = $props();
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
const projects = [
|
|
||||||
{
|
|
||||||
name: 'vash esports',
|
|
||||||
subtitle: 'the best way to play osu!',
|
|
||||||
href: 'https://esports.vash.software',
|
|
||||||
external: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'catchemall',
|
|
||||||
subtitle: 'catch 1025 pokemon across 53 games',
|
|
||||||
href: '/projects/catch-em-all',
|
|
||||||
external: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'animaldex',
|
|
||||||
subtitle: 'add neighborhood pets to the world map',
|
|
||||||
href: '/projects/animaldex',
|
|
||||||
external: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'osu! map finder',
|
|
||||||
subtitle: 'find osu! maps for Spotify songs/albums/playlists',
|
|
||||||
href: '/projects/spotify-beatmap-finder',
|
|
||||||
external: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'derby lens',
|
|
||||||
subtitle: 'estimate odds for horse betting',
|
|
||||||
href: '/projects/derby',
|
|
||||||
external: false
|
|
||||||
}
|
|
||||||
] as const;
|
|
||||||
const experience = [
|
const experience = [
|
||||||
{ when: '2026–now', role: 'associate software engineer', place: 'Cargill' },
|
{ when: '2026–now', role: 'associate software engineer', place: 'Cargill' },
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user