feat(header): icon nav redesign + migrate icons mdi→lucide
- Header desktop nav: social icons grouped in a wald-tinted pill with labels, active-state (aria-current + bold), opacity hover (no per-item bg) - Mobile overlay: social links as icon+label rows with active-state - Search button: lucide:search, icon-only - Migrate all icons mdi→lucide app-wide (234 refs); brand/language logos (google, whatsapp, mastodon, telegram, language-*) stay on mdi - generate-icons: emit mdi + lucide subsets; add @iconify-json/lucide - iconify-offline: register lucide collection - scripts/icon-map|validate|apply: reusable migration tooling Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -149,7 +149,7 @@
|
||||
aria-expanded="false"
|
||||
>
|
||||
{link.label}
|
||||
<Icon icon="mdi:chevron-down" class="size-3" aria-hidden="true" />
|
||||
<Icon icon="lucide:chevron-down" class="size-3" aria-hidden="true" />
|
||||
</a>
|
||||
{:else}
|
||||
<button
|
||||
@@ -159,7 +159,7 @@
|
||||
aria-expanded="false"
|
||||
>
|
||||
{link.label}
|
||||
<Icon icon="mdi:chevron-down" class="size-3" aria-hidden="true" />
|
||||
<Icon icon="lucide:chevron-down" class="size-3" aria-hidden="true" />
|
||||
</button>
|
||||
{/if}
|
||||
<div
|
||||
@@ -193,43 +193,51 @@
|
||||
</a>
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="w-1 h-4"></div>
|
||||
{#each socialLinks as social}
|
||||
{@const isInternal = social.href.startsWith("/")}
|
||||
<a
|
||||
href={social.href}
|
||||
target={isInternal ? undefined : "_blank"}
|
||||
rel={isInternal ? undefined : "noopener noreferrer"}
|
||||
class="p-1.5 -m-1.5 rounded-xs transition-colors flex items-center justify-center"
|
||||
aria-label={social.label || t(T.header_social_aria)}
|
||||
title={social.label || t(T.header_social_aria)}
|
||||
>
|
||||
<Icon icon={social.icon} aria-hidden="true" />
|
||||
</a>
|
||||
{/each}
|
||||
<button
|
||||
type="button"
|
||||
class="p-2 -mr-2 rounded-xs transition-colors flex items-center justify-center"
|
||||
aria-label={searchOpen
|
||||
? t(T.header_search_close)
|
||||
: t(T.header_search_open)}
|
||||
title={searchOpen
|
||||
? t(T.header_search_close)
|
||||
: t(T.header_search_open)}
|
||||
aria-expanded={searchOpen}
|
||||
aria-controls="search-panel"
|
||||
onclick={() => (searchOpen = !searchOpen)}
|
||||
<div
|
||||
class="ml-2 flex items-center gap-0.5 rounded-full border border-wald-700/15 bg-wald-700/8 px-2 py-1"
|
||||
>
|
||||
{#if searchOpen}
|
||||
<Icon
|
||||
icon="mdi:close"
|
||||
class="text-error!"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{:else}
|
||||
<Icon icon="mdi:magnify" aria-hidden="true" />
|
||||
{/if}
|
||||
</button>
|
||||
{#each socialLinks as social}
|
||||
{@const isInternal = social.href.startsWith("/")}
|
||||
{@const sActive = isInternal && isActiveLink(social.href, $page.url.pathname)}
|
||||
<a
|
||||
href={social.href}
|
||||
target={isInternal ? undefined : "_blank"}
|
||||
rel={isInternal ? undefined : "noopener noreferrer"}
|
||||
class="px-1.5 py-0.5 rounded-full transition-opacity hover:opacity-70 inline-flex items-center gap-1.5 {sActive ? 'font-bold' : ''}"
|
||||
aria-current={sActive ? "page" : undefined}
|
||||
aria-label={social.label || t(T.header_social_aria)}
|
||||
title={social.label || t(T.header_social_aria)}
|
||||
>
|
||||
<Icon icon={social.icon} class="text-sm" aria-hidden="true" />
|
||||
{#if social.label}
|
||||
<span class="text-[.7rem] leading-none">{social.label}</span>
|
||||
{/if}
|
||||
</a>
|
||||
{/each}
|
||||
<button
|
||||
type="button"
|
||||
class="px-1.5 py-0.5 rounded-full transition-opacity hover:opacity-70 inline-flex items-center justify-center"
|
||||
aria-label={searchOpen
|
||||
? t(T.header_search_close)
|
||||
: t(T.header_search_open)}
|
||||
title={searchOpen
|
||||
? t(T.header_search_close)
|
||||
: t(T.header_search_open)}
|
||||
aria-expanded={searchOpen}
|
||||
aria-controls="search-panel"
|
||||
onclick={() => (searchOpen = !searchOpen)}
|
||||
>
|
||||
{#if searchOpen}
|
||||
<Icon
|
||||
icon="lucide:x"
|
||||
class="text-sm text-error!"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{:else}
|
||||
<Icon icon="lucide:search" class="text-sm" aria-hidden="true" />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Mobile: Such- und Burger-Buttons (unter lg) -->
|
||||
@@ -246,13 +254,13 @@
|
||||
>
|
||||
{#if searchOpen}
|
||||
<Icon
|
||||
icon="mdi:close"
|
||||
icon="lucide:x"
|
||||
class="text-2xl text-error!"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{:else}
|
||||
<Icon
|
||||
icon="mdi:magnify"
|
||||
icon="lucide:search"
|
||||
class="text-2xl"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
@@ -275,12 +283,12 @@
|
||||
>
|
||||
{#if menuOpen}
|
||||
<Icon
|
||||
icon="mdi:close"
|
||||
icon="lucide:x"
|
||||
class="text-2xl"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{:else}
|
||||
<Icon icon="mdi:menu" class="text-2xl" aria-hidden="true" />
|
||||
<Icon icon="lucide:menu" class="text-2xl" aria-hidden="true" />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
@@ -314,7 +322,7 @@
|
||||
onclick={() => toggleSubmenu(link.label)}
|
||||
>
|
||||
<span>{link.label}</span>
|
||||
<Icon icon={expanded ? "mdi:chevron-up" : "mdi:chevron-down"} class="size-4" aria-hidden="true" />
|
||||
<Icon icon={expanded ? "lucide:chevron-up" : "lucide:chevron-down"} class="size-4" aria-hidden="true" />
|
||||
</button>
|
||||
{#if expanded}
|
||||
<div class="ml-6 border-l border-stein-0/10" in:slide={{ duration: 150 }} out:slide={{ duration: 100 }}>
|
||||
@@ -357,27 +365,34 @@
|
||||
{/if}
|
||||
{/each}
|
||||
{#if socialLinks.length > 0}
|
||||
<div
|
||||
class="flex flex-wrap gap-2 pt-2 mt-2 border-t border-stein-0/10"
|
||||
>
|
||||
<div class="flex flex-col gap-1 pt-2 mt-2 border-t border-stein-0/10">
|
||||
{#each socialLinks as social}
|
||||
{@const isInternal = social.href.startsWith("/")}
|
||||
{@const sActive =
|
||||
isInternal &&
|
||||
isActiveLink(social.href, $page.url.pathname)}
|
||||
<a
|
||||
href={social.href}
|
||||
target={isInternal ? undefined : "_blank"}
|
||||
rel={isInternal
|
||||
? undefined
|
||||
: "noopener noreferrer"}
|
||||
class="inline-flex p-2 text-stein-100 hover:text-stein-0 hover:bg-stein-0/10 rounded-xs transition-colors"
|
||||
class="flex items-center gap-3 py-4 px-3 rounded-xs transition-colors text-base border-l-4 {sActive
|
||||
? 'font-bold text-wald-300 bg-wald-700/20 border-wald-400'
|
||||
: 'text-stein-100 hover:text-stein-0 hover:bg-stein-0/10 border-transparent'}"
|
||||
aria-current={sActive ? "page" : undefined}
|
||||
aria-label={social.label ||
|
||||
t(T.header_social_aria)}
|
||||
onclick={closeMenu}
|
||||
>
|
||||
<Icon
|
||||
icon={social.icon}
|
||||
class="text-[1.5rem]"
|
||||
class="text-base"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{#if social.label}
|
||||
<span>{social.label}</span>
|
||||
{/if}
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user