Files
kontextwerk/frontend/src/lib/components/header/Header.svelte
2025-10-06 16:21:32 +00:00

234 lines
7.3 KiB
Svelte

<script lang="ts">
import { onMount } from "svelte"
import { spaLink } from "../../actions"
import { location } from "../../store"
import { headerLinks } from "../../../config"
import type { HeaderLink } from "../../../config"
let scrolled: boolean = $state(false)
const links: HeaderLink[] = headerLinks
function checkScroll() {
scrolled = window.scrollY >= 100
}
onMount(() => {
if (typeof window !== "undefined") {
checkScroll()
window.addEventListener("scroll", checkScroll)
return () => {
window.removeEventListener("scroll", checkScroll)
}
}
})
$effect(() => {
checkScroll()
})
let isHomepage = $derived($location?.path === "/")
let darkBG = $derived(scrolled || !isHomepage)
const resolveHref = (link: HeaderLink) => ("sectionId" in link ? `/#${link.sectionId}` : link.href)
</script>
<header
class="headercontainer"
id={"header-container"}
class:scrolled={darkBG}
class:homepageHeader={isHomepage}
role="dialog"
aria-label="Hauptnavigation"
>
<div class="padding">
<nav class="menu">
<a
href="/"
use:spaLink
class="logo-container"
aria-label="Go to homepage"
>
<img
src="../../../../logo/KontextWerk.svg"
alt="logo"
/>
</a>
<ul class="nav-links">
{#each links as link}
<li>
<a
href={resolveHref(link)}
use:spaLink
class="nav-link"
class:nav-link-button={link.type === "button"}
>
{link.label}
</a>
</li>
{/each}
</ul>
</nav>
</div>
</header>
<style lang="less">
@import "../../assets/css/variables.less";
@desktop: ~"only screen and (min-width: 1440px)";
.headercontainer {
display: flex;
flex-direction: column;
@media @mobile {
overflow: hidden;
}
position: sticky;
z-index: 5500;
top: 0px;
justify-content: space-between;
background-color: #0d0c0c;
align-items: center;
width: 100%;
.padding {
width: 100%;
padding: 0px var(--horizontal-default-margin);
height: 100%;
display: flex;
align-items: flex-end;
justify-content: center;
.menu {
max-width: var(--normal-max-width);
width: 100%;
display: flex;
height: 86px;
align-items: center;
justify-content: space-between;
.logo-container {
height: 64px;
display: flex;
align-items: flex-start;
img {
height: 60px;
width: auto;
object-fit: contain;
}
}
.nav-links {
display: flex;
align-items: center;
gap: 1.5rem;
list-style: none;
margin: 0;
padding: 0;
li {
display: flex;
align-items: center;
justify-content: center;
}
.nav-link {
color: var(--text-100);
text-decoration: none;
font-weight: 500;
font-size: 0.95rem;
transition:
color 0.2s ease,
background-color 0.2s ease,
border-color 0.2s ease;
padding: 0.5rem 0;
position: relative;
&:focus-visible {
outline: 2px solid var(--primary-200);
outline-offset: 3px;
}
&:after {
content: "";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 2px;
background: currentColor;
transform: scaleX(0);
transform-origin: left;
transition: transform 0.2s ease;
}
&:hover,
&:focus-visible {
color: var(--primary-200);
&:after {
transform: scaleX(1);
}
}
}
.nav-link-button {
border: 1px solid var(--primary-200);
border-radius: 4px;
padding: 0.45rem 1.4rem;
font-weight: 600;
color: var(--neutral-white);
background: linear-gradient(135deg, var(--primary-200), var(--primary-100));
box-shadow: 0 0 12px rgba(116, 30, 32, 0.45);
&:after {
display: none;
}
transition:
background 0.3s ease,
color 0.3s ease;
&:hover,
&:focus-visible {
color: var(--neutral-white);
background: linear-gradient(135deg, var(--primary-100), #ff5252);
}
}
}
}
}
&.homepageHeader {
background-color: transparent;
}
&.scrolled&.homepageHeader {
box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.2);
background-color: var(--bg-100);
}
}
@media @mobile {
.headercontainer {
.padding {
.menu {
flex-direction: column;
height: auto;
padding: 0.8rem 0;
gap: 0.8rem;
.logo-container {
height: 48px;
img {
height: 44px;
}
}
.nav-links {
flex-wrap: wrap;
justify-content: center;
gap: 0.8rem 1.2rem;
.nav-link {
padding: 0.4rem 0;
font-size: 0.9rem;
}
.nav-link-button {
padding: 0.4rem 1rem;
}
}
}
}
}
}
</style>