Compare commits

2 Commits

Author SHA1 Message Date
d4564ca973 scope ul font-size rule to prevent global CSS pollution
All checks were successful
CI / update (push) Successful in 9s
The global 'ul' selector was affecting all unordered lists across the app after visiting /glaube/rosenkranz. This caused layout issues with action buttons on recipe pages where the internal symbols would shift to the top instead of being centered.

Fixed by scoping the rule to only apply to ul elements within .gebet containers.
2025-12-27 13:31:40 +01:00
f161d8a15d migrate from deprecated slots to snippets and fix event handlers
- Replace deprecated <slot> syntax with modern {#snippet} and {@render} patterns
- Add TypeScript types for snippet props in Header component
- Convert on:click event handlers to onclick attribute throughout
- Update all layout files to use new snippet-based composition pattern
2025-12-27 12:24:30 +01:00
8 changed files with 112 additions and 60 deletions

View File

@@ -1,8 +1,23 @@
<script> <script lang="ts">
import "$lib/css/nordtheme.css" import "$lib/css/nordtheme.css"
import { onMount } from "svelte"; import { onMount } from "svelte";
import { page } from '$app/stores'; import { page } from '$app/stores';
import Symbol from "./Symbol.svelte" import Symbol from "./Symbol.svelte"
import type { Snippet } from 'svelte';
let {
links,
language_selector_mobile,
language_selector_desktop,
right_side,
children
}: {
links?: Snippet;
language_selector_mobile?: Snippet;
language_selector_desktop?: Snippet;
right_side?: Snippet;
children?: Snippet;
} = $props();
let underlineLeft = $state(0); let underlineLeft = $state(0);
let underlineWidth = $state(0); let underlineWidth = $state(0);
@@ -297,25 +312,25 @@ footer{
<div class=button_wrapper> <div class=button_wrapper>
<a href="/"><Symbol></Symbol></a> <a href="/"><Symbol></Symbol></a>
<div class="right-buttons"> <div class="right-buttons">
<slot name=language_selector_mobile></slot> {@render language_selector_mobile?.()}
<button class=nav_button on:click={() => {toggle_sidebar()}}><svg xmlns="http://www.w3.org/2000/svg" height="0.5em" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></button> <button class=nav_button onclick={() => {toggle_sidebar()}}><svg xmlns="http://www.w3.org/2000/svg" height="0.5em" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></button>
</div> </div>
</div> </div>
<nav hidden class=nav_site> <nav hidden class=nav_site>
<a class=entry href="/"><Symbol></Symbol></a> <a class=entry href="/"><Symbol></Symbol></a>
<div class="links-wrapper"> <div class="links-wrapper">
<slot name=links></slot> {@render links?.()}
<div class="active-underline" class:no-transition={disableTransition} style="left: {underlineLeft}px; width: {underlineWidth}px;"></div> <div class="active-underline" class:no-transition={disableTransition} style="left: {underlineLeft}px; width: {underlineWidth}px;"></div>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="language-selector-desktop"> <div class="language-selector-desktop">
<slot name=language_selector_desktop></slot> {@render language_selector_desktop?.()}
</div> </div>
<slot name=right_side></slot> {@render right_side?.()}
</div> </div>
</nav> </nav>
<slot></slot> {@render children?.()}
</div> </div>
<footer> <footer>

View File

@@ -136,7 +136,7 @@ h2 + p{
</style> </style>
{#if user} {#if user}
<button on:click={toggle_options} style="background-image: url(https://bocken.org/static/user/thumb/{user.nickname}.webp)" id=button> <button onclick={toggle_options} style="background-image: url(https://bocken.org/static/user/thumb/{user.nickname}.webp)" id=button>
<div id=options class="speech top" hidden> <div id=options class="speech top" hidden>
<h2>{user.name}</h2> <h2>{user.name}</h2>
<p>({user.nickname})</p> <p>({user.nickname})</p>

View File

@@ -2,7 +2,7 @@ div.gebet{
text-align: center; text-align: center;
font-size: 1.25em; font-size: 1.25em;
} }
ul { .gebet ul {
font-size: 120%; font-size: 120%;
} }
.gebet v{ .gebet v{

View File

@@ -2,16 +2,28 @@
import Header from '$lib/components/Header.svelte' import Header from '$lib/components/Header.svelte'
import UserHeader from '$lib/components/UserHeader.svelte'; import UserHeader from '$lib/components/UserHeader.svelte';
import LanguageSelector from '$lib/components/LanguageSelector.svelte'; import LanguageSelector from '$lib/components/LanguageSelector.svelte';
let { data } = $props(); let { data, children } = $props();
let user = $derived(data.session?.user); let user = $derived(data.session?.user);
</script> </script>
<Header> <Header>
<ul class=site_header slot=links> {#snippet links()}
<ul class=site_header>
</ul> </ul>
<LanguageSelector slot=language_selector_mobile /> {/snippet}
<LanguageSelector slot=language_selector_desktop />
<UserHeader {user} slot=right_side></UserHeader> {#snippet language_selector_mobile()}
<slot></slot> <LanguageSelector />
{/snippet}
{#snippet language_selector_desktop()}
<LanguageSelector />
{/snippet}
{#snippet right_side()}
<UserHeader {user}></UserHeader>
{/snippet}
{@render children()}
</Header> </Header>

View File

@@ -80,8 +80,10 @@
</svelte:head> </svelte:head>
<Header> <Header>
<ul class="site_header" slot="links"> {#snippet links()}
<ul class="site_header">
</ul> </ul>
{/snippet}
<main class="error-page"> <main class="error-page">
<div class="error-container"> <div class="error-container">
@@ -109,38 +111,38 @@
<div class="error-actions"> <div class="error-actions">
{#if status === 401} {#if status === 401}
<button class="btn btn-primary" on:click={login}> <button class="btn btn-primary" onclick={login}>
Anmelden Anmelden
</button> </button>
<button class="btn btn-secondary" on:click={goHome}> <button class="btn btn-secondary" onclick={goHome}>
Zur Startseite Zur Startseite
</button> </button>
{:else if status === 403} {:else if status === 403}
<button class="btn btn-primary" on:click={goHome}> <button class="btn btn-primary" onclick={goHome}>
Zur Startseite Zur Startseite
</button> </button>
<button class="btn btn-secondary" on:click={goBack}> <button class="btn btn-secondary" onclick={goBack}>
Zurück Zurück
</button> </button>
{:else if status === 404} {:else if status === 404}
<button class="btn btn-primary" on:click={goHome}> <button class="btn btn-primary" onclick={goHome}>
Zur Startseite Zur Startseite
</button> </button>
<button class="btn btn-secondary" on:click={goBack}> <button class="btn btn-secondary" onclick={goBack}>
Zurück Zurück
</button> </button>
{:else if status === 500} {:else if status === 500}
<button class="btn btn-primary" on:click={goHome}> <button class="btn btn-primary" onclick={goHome}>
Zur Startseite Zur Startseite
</button> </button>
<button class="btn btn-secondary" on:click={goBack}> <button class="btn btn-secondary" onclick={goBack}>
Erneut versuchen Erneut versuchen
</button> </button>
{:else} {:else}
<button class="btn btn-primary" on:click={goHome}> <button class="btn btn-primary" onclick={goHome}>
Zur Startseite Zur Startseite
</button> </button>
<button class="btn btn-secondary" on:click={goBack}> <button class="btn btn-secondary" onclick={goBack}>
Zurück Zurück
</button> </button>
{/if} {/if}

View File

@@ -3,7 +3,7 @@ import { page } from '$app/stores';
import Header from '$lib/components/Header.svelte' import Header from '$lib/components/Header.svelte'
import UserHeader from '$lib/components/UserHeader.svelte'; import UserHeader from '$lib/components/UserHeader.svelte';
import LanguageSelector from '$lib/components/LanguageSelector.svelte'; import LanguageSelector from '$lib/components/LanguageSelector.svelte';
let { data } = $props(); let { data, children } = $props();
let user = $derived(data.session?.user); let user = $derived(data.session?.user);
@@ -29,7 +29,8 @@ function isActive(path) {
</script> </script>
<Header> <Header>
<ul class=site_header slot=links> {#snippet links()}
<ul class=site_header>
<li><a href="/{data.recipeLang}" class:active={isActive(`/${data.recipeLang}`)}>{labels.allRecipes}</a></li> <li><a href="/{data.recipeLang}" class:active={isActive(`/${data.recipeLang}`)}>{labels.allRecipes}</a></li>
{#if user} {#if user}
<li><a href="/{data.recipeLang}/favorites" class:active={isActive(`/${data.recipeLang}/favorites`)}>{labels.favorites}</a></li> <li><a href="/{data.recipeLang}/favorites" class:active={isActive(`/${data.recipeLang}/favorites`)}>{labels.favorites}</a></li>
@@ -39,8 +40,19 @@ function isActive(path) {
<li><a href="/{data.recipeLang}/icon" class:active={isActive(`/${data.recipeLang}/icon`)}>{labels.icon}</a></li> <li><a href="/{data.recipeLang}/icon" class:active={isActive(`/${data.recipeLang}/icon`)}>{labels.icon}</a></li>
<li><a href="/{data.recipeLang}/tag" class:active={isActive(`/${data.recipeLang}/tag`)}>{labels.keywords}</a></li> <li><a href="/{data.recipeLang}/tag" class:active={isActive(`/${data.recipeLang}/tag`)}>{labels.keywords}</a></li>
</ul> </ul>
<LanguageSelector slot=language_selector_mobile /> {/snippet}
<LanguageSelector slot=language_selector_desktop />
<UserHeader slot=right_side {user}></UserHeader> {#snippet language_selector_mobile()}
<slot></slot> <LanguageSelector />
{/snippet}
{#snippet language_selector_desktop()}
<LanguageSelector />
{/snippet}
{#snippet right_side()}
<UserHeader {user}></UserHeader>
{/snippet}
{@render children()}
</Header> </Header>

View File

@@ -8,7 +8,7 @@
import Header from '$lib/components/Header.svelte'; import Header from '$lib/components/Header.svelte';
import UserHeader from '$lib/components/UserHeader.svelte'; import UserHeader from '$lib/components/UserHeader.svelte';
export let data; let { data, children } = $props();
let showModal = false; let showModal = false;
let paymentId = null; let paymentId = null;
@@ -57,16 +57,21 @@
</script> </script>
<Header> <Header>
<ul class="site_header" slot="links"> {#snippet links()}
<ul class="site_header">
<li><a href="/cospend" class:active={isActive('/cospend')}>Dashboard</a></li> <li><a href="/cospend" class:active={isActive('/cospend')}>Dashboard</a></li>
<li><a href="/cospend/payments" class:active={isActive('/cospend/payments')}>All Payments</a></li> <li><a href="/cospend/payments" class:active={isActive('/cospend/payments')}>All Payments</a></li>
<li><a href="/cospend/recurring" class:active={isActive('/cospend/recurring')}>Recurring Payments</a></li> <li><a href="/cospend/recurring" class:active={isActive('/cospend/recurring')}>Recurring Payments</a></li>
</ul> </ul>
<UserHeader slot="right_side" {user}></UserHeader> {/snippet}
{#snippet right_side()}
<UserHeader {user}></UserHeader>
{/snippet}
<div class="layout-container" class:has-modal={showModal}> <div class="layout-container" class:has-modal={showModal}>
<div class="main-content"> <div class="main-content">
<slot /> {@render children()}
</div> </div>
<div class="side-panel"> <div class="side-panel">

View File

@@ -2,7 +2,7 @@
import { page } from '$app/stores'; import { page } from '$app/stores';
import Header from '$lib/components/Header.svelte' import Header from '$lib/components/Header.svelte'
import UserHeader from '$lib/components/UserHeader.svelte'; import UserHeader from '$lib/components/UserHeader.svelte';
export let data let { data, children } = $props();
function isActive(path) { function isActive(path) {
const currentPath = $page.url.pathname; const currentPath = $page.url.pathname;
@@ -11,11 +11,17 @@ function isActive(path) {
} }
</script> </script>
<Header> <Header>
<ul class=site_header slot=links> {#snippet links()}
<ul class=site_header>
<li><a href="/glaube/gebete" class:active={isActive('/glaube/gebete')}>Gebete</a></li> <li><a href="/glaube/gebete" class:active={isActive('/glaube/gebete')}>Gebete</a></li>
<li><a href="/glaube/rosenkranz" class:active={isActive('/glaube/rosenkranz')}>Rosenkranz</a></li> <li><a href="/glaube/rosenkranz" class:active={isActive('/glaube/rosenkranz')}>Rosenkranz</a></li>
<li><a href="/glaube/predigten" class:active={isActive('/glaube/predigten')}>Predigten</a></li> <li><a href="/glaube/predigten" class:active={isActive('/glaube/predigten')}>Predigten</a></li>
</ul> </ul>
<UserHeader user={data.session?.user} slot=right_side></UserHeader> {/snippet}
<slot></slot>
{#snippet right_side()}
<UserHeader user={data.session?.user}></UserHeader>
{/snippet}
{@render children()}
</Header> </Header>