fix(web): center nav in window + Library link + Search placeholder
test-web / test (push) Failing after 31s
test-web / test (push) Failing after 31s
Three operator-reported issues:
- Header was flex with the nav inside a flex-1 span — when the
right side (search + user menu) grew wider than the left
(wordmark), the nav's centered position drifted off page-center.
Switched to grid-cols-3 with justify-self-{start,center,end} so
the middle column pins to true window-center regardless of side
widths.
- Library nav link pointed to /library, which 308-redirects via
+page.server.ts. Operator reported it didn't navigate. Linked the
nav button directly to /library/artists so SPA navigation skips
the redirect roundtrip. matchPrefix='/library' keeps isActive
matching every Library tab.
- SearchInput placeholder was 'Search artists, albums, tracks…' —
shortened to 'Search' and added a Lucide Search icon inside the
input on the left. Padding adjusted (pl-7) so the input text
clears the icon.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import { page } from '$app/state';
|
||||
import { goto } from '$app/navigation';
|
||||
import { untrack } from 'svelte';
|
||||
import { Search } from 'lucide-svelte';
|
||||
|
||||
let value = $state(page.url.searchParams.get('q') ?? '');
|
||||
let timeout: ReturnType<typeof setTimeout> | null = null;
|
||||
@@ -51,12 +52,20 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<input
|
||||
type="search"
|
||||
placeholder="Search artists, albums, tracks…"
|
||||
aria-label="Search"
|
||||
bind:value
|
||||
oninput={onInput}
|
||||
onkeydown={onKey}
|
||||
class="w-full max-w-md rounded border border-border bg-surface px-3 py-1 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent"
|
||||
/>
|
||||
<label class="relative block w-full max-w-md">
|
||||
<Search
|
||||
size={14}
|
||||
strokeWidth={1.5}
|
||||
aria-hidden="true"
|
||||
class="pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 text-text-secondary"
|
||||
/>
|
||||
<input
|
||||
type="search"
|
||||
placeholder="Search"
|
||||
aria-label="Search"
|
||||
bind:value
|
||||
oninput={onInput}
|
||||
onkeydown={onKey}
|
||||
class="w-full rounded border border-border bg-surface pl-7 pr-3 py-1 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent"
|
||||
/>
|
||||
</label>
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
goto('/login', { replaceState: true });
|
||||
}
|
||||
|
||||
function isActive(href: string): boolean {
|
||||
if (href === '/') return page.url.pathname === '/';
|
||||
return page.url.pathname.startsWith(href);
|
||||
function isActive(prefix: string): boolean {
|
||||
if (prefix === '/') return page.url.pathname === '/';
|
||||
return page.url.pathname.startsWith(prefix);
|
||||
}
|
||||
|
||||
// Primary nav. Sits centered in the top bar (replaces the prior left
|
||||
@@ -46,22 +46,33 @@
|
||||
// destinations, plus Library acting as a parent for Artists / Albums /
|
||||
// Liked / History / Playlists (tabs at /library/+layout.svelte).
|
||||
// Search + user menu live on the right side of the header (unchanged).
|
||||
// Library targets the Artists tab directly so SPA navigation doesn't
|
||||
// bounce through the /library 308-redirect. isActive('/library')
|
||||
// still matches all sub-routes by prefix so the active state covers
|
||||
// every tab.
|
||||
const navItems = [
|
||||
{ href: '/', label: 'Home', icon: House },
|
||||
{ href: '/library', label: 'Library', icon: LibraryBig },
|
||||
{ href: '/discover', label: 'Discover', icon: Compass }
|
||||
{ href: '/', label: 'Home', icon: House, matchPrefix: '/' },
|
||||
{ href: '/library/artists', label: 'Library', icon: LibraryBig, matchPrefix: '/library' },
|
||||
{ href: '/discover', label: 'Discover', icon: Compass, matchPrefix: '/discover' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<svelte:window onclick={handleWindowClick} onkeydown={(e) => e.key === 'Escape' && (menuOpen = false)} />
|
||||
|
||||
<div class="grid h-screen grid-rows-[auto_1fr_auto] bg-background text-text-primary">
|
||||
<header class="flex items-center gap-3 md:gap-6 border-b border-border bg-surface px-3 md:px-4 py-2">
|
||||
<a href="/" class="font-semibold text-sm md:text-base whitespace-nowrap">{appName()}</a>
|
||||
<!-- 3-col grid so the middle column (nav) is centered on the page
|
||||
regardless of how wide the side columns are. With the prior
|
||||
flex+justify-center layout the nav's "available space" shifted
|
||||
whenever search or the user menu grew, so the nav drifted off
|
||||
window-center. Grid pins each column to a fixed lane. -->
|
||||
<header class="grid grid-cols-3 items-center border-b border-border bg-surface px-3 md:px-4 py-2 gap-3 md:gap-6">
|
||||
<a href="/" class="font-semibold text-sm md:text-base whitespace-nowrap justify-self-start">
|
||||
{appName()}
|
||||
</a>
|
||||
|
||||
<nav aria-label="Primary" class="flex flex-1 items-center justify-center gap-1 md:gap-2">
|
||||
<nav aria-label="Primary" class="flex items-center justify-center gap-1 md:gap-2 justify-self-center">
|
||||
{#each navItems as item}
|
||||
{@const active = isActive(item.href)}
|
||||
{@const active = isActive(item.matchPrefix)}
|
||||
<a
|
||||
href={item.href}
|
||||
class="group flex items-center gap-2 rounded px-2 md:px-3 py-2 min-h-[44px]
|
||||
@@ -76,6 +87,7 @@
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
<div class="flex items-center gap-3 md:gap-6 justify-self-end min-w-0">
|
||||
<div class="hidden md:block min-w-0 max-w-xs flex-shrink">
|
||||
<SearchInput />
|
||||
</div>
|
||||
@@ -133,6 +145,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Subtle 1200x600 radial highlight at the top of the scroll
|
||||
|
||||
Reference in New Issue
Block a user