css: consolidate stylesheets into single source of truth
All checks were successful
CI / update (push) Successful in 1m29s

Merge nordtheme.css tokens and utility classes into app.css, import
app.css once in root layout, delete redundant files (nordtheme.css,
form.css, rosenkranz.css), move domain CSS to layouts, fix broken
shake keyframe in action_button.css, and scope form styles to the
two pages that need them. 10 CSS files → 6, 41 redundant imports removed.
This commit is contained in:
2026-02-15 22:26:22 +01:00
parent a435a1142f
commit 4191012cf1
47 changed files with 209 additions and 553 deletions

View File

@@ -109,6 +109,35 @@
--color-warning: var(--nord13); --color-warning: var(--nord13);
--color-error: var(--nord11); --color-error: var(--nord11);
--color-info: var(--nord10); --color-info: var(--nord10);
/* Shared transitions & shadows */
--transition-fast: 100ms;
--transition-normal: 200ms;
--shadow-sm: 0 0 0.4em 0.05em rgba(0,0,0,0.2);
--shadow-md: 0 0 0.5em 0.1em rgba(0,0,0,0.3);
--shadow-lg: 0 0 1em 0.1em rgba(0,0,0,0.4);
--shadow-hover: 0.1em 0.1em 0.5em 0.1em rgba(0,0,0,0.3);
--radius-pill: 1000px;
--radius-card: 20px;
--radius-sm: 0.3rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
/* Spacing scale */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 1.5rem;
--space-xl: 2rem;
--space-2xl: 3rem;
/* Font size scale */
--text-sm: 0.85rem;
--text-base: 1rem;
--text-lg: 1.1rem;
--text-xl: 1.5rem;
--text-2xl: 2rem;
--text-3xl: 3rem;
} }
/* ============================================ /* ============================================
@@ -208,65 +237,110 @@ a:focus-visible {
color: var(--color-link-hover); color: var(--color-link-hover);
} }
/* ============================================ /* ============================================
FORM STYLES GLOBAL UTILITY CLASSES
============================================ */ ============================================ */
form { /* Pill-shaped element base */
background-color: var(--color-bg-secondary); .g-pill {
display: flex; border-radius: var(--radius-pill);
flex-direction: column;
max-width: 600px;
gap: 0.5em;
margin-inline: auto;
justify-content: center;
align-items: center;
padding-block: 2rem;
margin-block: 2rem;
}
form label {
font-size: 1.2em;
}
form input {
display: block;
font-size: 1.2rem;
}
form:not(.search) button {
background-color: var(--color-accent);
color: var(--color-text-on-accent);
border: none; border: none;
padding: 0.5em 1em;
font-size: 1.3em;
border-radius: 1000px;
margin-top: 1em;
transition: 100ms;
cursor: pointer; cursor: pointer;
display: inline-block;
text-decoration: none;
transition: var(--transition-fast);
} }
form:not(.search) button:hover, /* Interactive hover/focus effects */
form:not(.search) button:focus-visible { .g-interactive {
background-color: var(--color-accent-hover); transition: var(--transition-fast);
scale: 1.1; }
.g-interactive:hover,
.g-interactive:focus-visible {
transform: scale(1.05);
box-shadow: var(--shadow-hover);
}
.g-interactive:focus {
scale: 0.9;
} }
form:not(.search) button:active { /* Light background button (with dark mode) */
background-color: var(--color-accent-active); .g-btn-light {
background-color: var(--nord5);
color: var(--nord0);
box-shadow: var(--shadow-sm);
} }
@media (prefers-color-scheme: dark) {
form p { .g-btn-light {
max-width: 400px; background-color: var(--nord0);
margin-top: 0; color: white;
} }
}
form h4 {
margin-bottom: 0; /* Dark background button */
} .g-btn-dark,
.g-btn-dark:visited,
@media screen and (max-width: 600px) { .g-btn-dark:link {
form { background-color: var(--nord0);
margin-top: 0; color: var(--nord6);
box-shadow: var(--shadow-lg);
}
.g-btn-dark:hover,
.g-btn-dark:focus-visible {
background-color: var(--nord1);
color: var(--nord6);
}
/* Icon badge (circular icon container) */
.g-icon-badge {
font-family: "Noto Color Emoji", emoji, sans-serif;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
text-decoration: none;
transition: var(--transition-fast);
box-shadow: var(--shadow-lg);
}
.g-icon-badge:hover,
.g-icon-badge:focus-visible {
transform: scale(1.1);
box-shadow: var(--shadow-hover);
}
/* Tag/chip styling */
.g-tag,
.g-tag:visited,
.g-tag:link {
font-size: 1.1rem;
padding: 0.25em 1em;
border-radius: var(--radius-pill);
background-color: var(--nord5);
color: var(--nord0);
text-decoration: none;
cursor: pointer;
transition: var(--transition-fast);
box-shadow: var(--shadow-sm);
border: none;
display: inline-block;
}
.g-tag:hover,
.g-tag:focus-visible {
transform: scale(1.05);
background-color: var(--nord8);
box-shadow: var(--shadow-hover);
color: var(--nord0);
}
@media (prefers-color-scheme: dark) {
.g-tag,
.g-tag:visited,
.g-tag:link {
background-color: var(--nord0);
color: white;
}
.g-tag:hover,
.g-tag:focus-visible {
color: var(--nord0);
} }
} }

View File

@@ -2,7 +2,6 @@
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
let { href, ariaLabel = undefined, children } = $props<{ href: string, ariaLabel?: string, children?: Snippet }>(); let { href, ariaLabel = undefined, children } = $props<{ href: string, ariaLabel?: string, children?: Snippet }>();
import "$lib/css/nordtheme.css"
import "$lib/css/action_button.css" import "$lib/css/action_button.css"
</script> </script>

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
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"

View File

@@ -1,5 +1,4 @@
<script> <script>
import "$lib/css/nordtheme.css";
let { let {
value = $bindable(''), value = $bindable(''),

View File

@@ -1,5 +1,4 @@
<script> <script>
import "$lib/css/nordtheme.css";
</script> </script>
<style> <style>
:root{ :root{

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
let { tag, ref } = $props<{ tag: string, ref: string }>(); let { tag, ref } = $props<{ tag: string, ref: string }>();
import '$lib/css/nordtheme.css'
</script> </script>
<style> <style>
a{ a{

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import "$lib/css/nordtheme.css";
import "$lib/css/shake.css"; import "$lib/css/shake.css";
import "$lib/css/icon.css"; import "$lib/css/icon.css";
import { onMount } from "svelte"; import { onMount } from "svelte";

View File

@@ -1,5 +1,4 @@
<script> <script>
import "$lib/css/nordtheme.css";
import TagChip from '$lib/components/recipes/TagChip.svelte'; import TagChip from '$lib/components/recipes/TagChip.svelte';
let { let {

View File

@@ -5,7 +5,6 @@ import Cross from '$lib/assets/icons/Cross.svelte'
import Plus from '$lib/assets/icons/Plus.svelte' import Plus from '$lib/assets/icons/Plus.svelte'
import Check from '$lib/assets/icons/Check.svelte' import Check from '$lib/assets/icons/Check.svelte'
import '$lib/css/nordtheme.css'
import "$lib/css/action_button.css" import "$lib/css/action_button.css"
import { do_on_key } from '$lib/components/recipes/do_on_key.js' import { do_on_key } from '$lib/components/recipes/do_on_key.js'

View File

@@ -1,5 +1,4 @@
<script> <script>
import "$lib/css/nordtheme.css";
import Toggle from '$lib/components/Toggle.svelte'; import Toggle from '$lib/components/Toggle.svelte';
let { let {

View File

@@ -1,5 +1,4 @@
<script> <script>
import "$lib/css/nordtheme.css";
import CategoryFilter from './CategoryFilter.svelte'; import CategoryFilter from './CategoryFilter.svelte';
import TagFilter from './TagFilter.svelte'; import TagFilter from './TagFilter.svelte';
import IconFilter from './IconFilter.svelte'; import IconFilter from './IconFilter.svelte';

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import '$lib/css/nordtheme.css';
import "$lib/css/shake.css" import "$lib/css/shake.css"
let { icon, ...restProps } = $props<{ icon: string, [key: string]: any }>(); let { icon, ...restProps } = $props<{ icon: string, [key: string]: any }>();
</script> </script>

View File

@@ -1,5 +1,4 @@
<script> <script>
import "$lib/css/nordtheme.css";
import TagChip from '$lib/components/recipes/TagChip.svelte'; import TagChip from '$lib/components/recipes/TagChip.svelte';
let { let {

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
import '$lib/css/nordtheme.css';
import Recipes from '$lib/components/recipes/Recipes.svelte'; import Recipes from '$lib/components/recipes/Recipes.svelte';
import Search from './Search.svelte'; import Search from './Search.svelte';

View File

@@ -4,8 +4,6 @@ import { onNavigate } from "$app/navigation";
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import { page } from '$app/stores'; import { page } from '$app/stores';
import HefeSwapper from './HefeSwapper.svelte'; import HefeSwapper from './HefeSwapper.svelte';
import '$lib/css/recipe-links.css';
let { data } = $props(); let { data } = $props();
// Helper function to multiply numbers in ingredient amounts // Helper function to multiply numbers in ingredient amounts

View File

@@ -1,5 +1,4 @@
<script> <script>
import '$lib/css/recipe-links.css';
let { data } = $props(); let { data } = $props();
let multiplier = $state(data.multiplier || 1); let multiplier = $state(data.multiplier || 1);

View File

@@ -1,5 +1,4 @@
<script> <script>
import "$lib/css/nordtheme.css";
let { let {
useAndLogic = true, useAndLogic = true,

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
import "$lib/css/nordtheme.css"
let { title = '', children } = $props<{ title?: string, children?: Snippet }>(); let { title = '', children } = $props<{ title?: string, children?: Snippet }>();
</script> </script>
<style> <style>

View File

@@ -1,7 +1,6 @@
<script> <script>
import {onMount} from "svelte"; import {onMount} from "svelte";
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import "$lib/css/nordtheme.css";
import FilterPanel from './FilterPanel.svelte'; import FilterPanel from './FilterPanel.svelte';
import { getCategories } from '$lib/js/categories'; import { getCategories } from '$lib/js/categories';

View File

@@ -1,5 +1,4 @@
<script> <script>
import "$lib/css/nordtheme.css";
import TagChip from '$lib/components/recipes/TagChip.svelte'; import TagChip from '$lib/components/recipes/TagChip.svelte';
let { let {

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
import '$lib/css/nordtheme.css';
import Recipes from '$lib/components/recipes/Recipes.svelte'; import Recipes from '$lib/components/recipes/Recipes.svelte';
import Search from './Search.svelte'; import Search from './Search.svelte';

View File

@@ -1,5 +1,4 @@
<script lang=ts> <script lang=ts>
import "$lib/css/nordtheme.css"
import { season } from '$lib/js/season_store.js' import { season } from '$lib/js/season_store.js'
import {onMount} from "svelte"; import {onMount} from "svelte";
import {do_on_key} from "./do_on_key"; import {do_on_key} from "./do_on_key";

View File

@@ -1,5 +1,4 @@
<script> <script>
import "$lib/css/nordtheme.css";
let { let {
tag = '', tag = '',

View File

@@ -1,5 +1,4 @@
<script> <script>
import "$lib/css/nordtheme.css";
import TagChip from '$lib/components/recipes/TagChip.svelte'; import TagChip from '$lib/components/recipes/TagChip.svelte';
let { let {

View File

@@ -1,3 +1,5 @@
@import "./shake.css";
:root{ :root{
--angle: 15deg; --angle: 15deg;
} }
@@ -26,33 +28,3 @@
transition: 50ms; transition: 50ms;
scale: 0.8 0.8; scale: 0.8 0.8;
} }
@keyframes shake{
0%{
transform: rotate(0)
scale(1,1);
}
25%{
box-shadow: 0em 0em 1em 0.2em rgba(0, 0, 0, 0.6);
transform: rotate(--angle)
scale(1.2,1.2)
;
}
50%{
box-shadow: 0em 0em 1em 0.2em rgba(0, 0, 0, 0.6);
transform: rotate(calc(-1 * --angle))
scale(1.2,1.2);
}
74%{
box-shadow: 0em 0em 1em 0.2em rgba(0, 0, 0, 0.6);
transform: rotate(--angle)
scale(1.2, 1.2);
}
100%{
transform: rotate(0)
scale(1.2, 1.2);
}
}

View File

@@ -1,51 +0,0 @@
form{
background-color: var(--nord5);
display: flex;
flex-direction: column;
max-width: 600px;
gap: 0.5em;
margin-inline: auto;
justify-content: center;
align-items: center;
padding-block: 2rem;
margin-block: 2rem;
}
@media (prefers-color-scheme: dark){
form{
background-color: var(--accent-dark);
}
}
form label{
font-size: 1.2em;
}
form input{
display: block;
font-size: 1.2rem;
}
form button{
background-color: var(--red);
color: white;
border: none;
padding: 0.5em 1em;
font-size: 1.3em;
border-radius: 1000px;
margin-top: 1em;
transition: 100ms;
}
form button:hover,
form button:focus-visible
{
scale: 1.1;
}
form p{
max-width: 400px;
margin-top: 0;
}
form h4{
margin-bottom:0;
}
@media screen and (max-width: 600px){
form{
margin-top: 0;
}
}

View File

@@ -1,178 +0,0 @@
:root{
--nord0: #2E3440;
--nord1: #3B4252;
--nord2: #434C5E;
--nord3: #4C566A;
--nord4: #D8DEE9;
--nord5: #E5E9F0;
--nord6: #ECEFF4;
--nord7: #8FBCBB;
--nord8: #88C0D0;
--nord9: #81A1C1;
--nord10: #5E81AC;
--nord11: #BF616A;
--nord12: #D08770;
--nord13: #EBCB8B;
--nord14: #A3BE8C;
--nord15: #B48EAD;
--lightblue: var(--nord9);
--blue: var(--nord10);
--red: var(--nord11);
--orange: var(--nord12);
--yellow: var(--nord13);
--green: var(--nord14);
--purple: var(--nord15);
--nord6-dark: #292c31;
--accent-dark: #1f1f21;
--background-dark: #21201b;
--font-default-dark: #ffffff;
/* Shared transitions & shadows */
--transition-fast: 100ms;
--transition-normal: 200ms;
--shadow-sm: 0 0 0.4em 0.05em rgba(0,0,0,0.2);
--shadow-md: 0 0 0.5em 0.1em rgba(0,0,0,0.3);
--shadow-lg: 0 0 1em 0.1em rgba(0,0,0,0.4);
--shadow-hover: 0.1em 0.1em 0.5em 0.1em rgba(0,0,0,0.3);
--radius-pill: 1000px;
--radius-card: 20px;
--radius-sm: 0.3rem;
}
a:not(:visited){
color: var(--blue);
}
a:visited{
color: var(--purple);
}
@media (prefers-color-scheme: dark) {
a:not(:visited){
color: var(--nord8);
}
}
*{
box-sizing: border-box;
font-family: Helvetica, Arial, "Noto Sans", sans-serif
}
body{
margin:0;
padding:0;
background-color: #fbf9f3;
overflow-x: hidden;
}
@media (prefers-color-scheme: dark) {
body{
color: white;
background-color: var(--background-dark);
}
}
/* ========================================
Global Utility Classes
Use these in components to avoid CSS duplication
======================================== */
/* Pill-shaped element base */
.g-pill {
border-radius: var(--radius-pill);
border: none;
cursor: pointer;
display: inline-block;
text-decoration: none;
transition: var(--transition-fast);
}
/* Interactive hover/focus effects */
.g-interactive {
transition: var(--transition-fast);
}
.g-interactive:hover,
.g-interactive:focus-visible {
transform: scale(1.05);
box-shadow: var(--shadow-hover);
}
.g-interactive:focus {
scale: 0.9;
}
/* Light background button (with dark mode) */
.g-btn-light {
background-color: var(--nord5);
color: var(--nord0);
box-shadow: var(--shadow-sm);
}
@media (prefers-color-scheme: dark) {
.g-btn-light {
background-color: var(--nord0);
color: white;
}
}
/* Dark background button */
.g-btn-dark,
.g-btn-dark:visited,
.g-btn-dark:link {
background-color: var(--nord0);
color: var(--nord6);
box-shadow: var(--shadow-lg);
}
.g-btn-dark:hover,
.g-btn-dark:focus-visible {
background-color: var(--nord1);
color: var(--nord6);
}
/* Icon badge (circular icon container) */
.g-icon-badge {
font-family: "Noto Color Emoji", emoji, sans-serif;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
text-decoration: none;
transition: var(--transition-fast);
box-shadow: var(--shadow-lg);
}
.g-icon-badge:hover,
.g-icon-badge:focus-visible {
transform: scale(1.1);
box-shadow: var(--shadow-hover);
}
/* Tag/chip styling */
.g-tag,
.g-tag:visited,
.g-tag:link {
font-size: 1.1rem;
padding: 0.25em 1em;
border-radius: var(--radius-pill);
background-color: var(--nord5);
color: var(--nord0);
text-decoration: none;
cursor: pointer;
transition: var(--transition-fast);
box-shadow: var(--shadow-sm);
border: none;
display: inline-block;
}
.g-tag:hover,
.g-tag:focus-visible {
transform: scale(1.05);
background-color: var(--nord8);
box-shadow: var(--shadow-hover);
color: var(--nord0);
}
@media (prefers-color-scheme: dark) {
.g-tag,
.g-tag:visited,
.g-tag:link {
background-color: var(--nord0);
color: white;
}
.g-tag:hover,
.g-tag:focus-visible {
color: var(--nord0);
}
}

View File

@@ -1,198 +0,0 @@
@font-face {
font-family: 'LibertineMinimal';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(/fonts/LinLibertine_minimal.woff2) format('woff2'),
url(/fonts/LinLibertine_minimal.ttf) format('truetype');
}
.sbeads{
fill: var(--nord10);
}
.chain{
stroke:black;
stroke-width: 0.7;
stroke-miterlimit: 4;
stroke: gray;
fill: none;
}
.sbeads circle.hitbox{
r: 3.2px;
stroke-width:0;
}
#start1 circle{
cx:15.559271px;
cy: 20.881956px;
}
#start2 circle{
cx:21.633902px;
cy:20.367514px;
}
#start3 circle{
cx:27.96961px;
cy:21.178484px;
}
#lbead5 circle{
cx:118.50725px;
cy:59.477211px;
}
#lbead4 circle{
cx:126.81134px;
cy:15.751753px;
}
#lbead1 circle{
cx:7.6719489px;
cy:25.364584px;
}
#lbead2 circle{
cx:36.798512px;
cy:23.486462px;
}
#lbead3 circle{
cx:84.105789px;
cy:3.0456686px;
}
#lbead6 circle{
cx:72.185097px;
cy:64.006859px;
}
#start1:hover .msg,
#start2:hover .msg,
#start3:hover .msg,
#secret1:hover .msg,
#secret2:hover .msg,
#secret3:hover .msg,
#secret4:hover .msg,
#secret5:hover .msg,
#lbeads .beforedecades:hover .msg,
#lbeads .afterdecade:hover .msg,
#cross:hover .msg
{
display:block;
}
#start1:hover .sbeads circle:not(.hitbox),
#start2:hover .sbeads circle:not(.hitbox),
#start3:hover .sbeads circle:not(.hitbox),
#secret1:hover .sbeads circle,
#secret2:hover .sbeads circle,
#secret3:hover .sbeads circle,
#secret4:hover .sbeads circle,
#secret5:hover .sbeads circle
{
fill: var(--nord11);
r: 1.5px;
}
#lbead1:hover .lbead,
#lbead2:hover .lbead,
#lbead3:hover .lbead,
#lbead4:hover .lbead,
#lbead5:hover .lbead,
#lbead6:hover .lbead{
r: 2.8px;
fill: var(--nord11);
}
#cross:hover .symbol{
fill: var(--nord11);
stroke: var(--nord11);
stroke-width: 0.25;
}
#lbeads.msg{
display:block;
}
.sbeads circle{
r: 1.25px;
}
.msg .diff{
fill: var(--nord11);
}
.msg .b{
font-family: crosses;
font-weight: bold;
}
.msg .title{
fill: var(--nord10);
font-weight: bold;
font-size: 5px;
}
.msg{
font-size: 4px;
stroke: none;
fill: var(--nord4);
display:none;
}
text{
font-family: LibertineMinimal;
}
#lbeads circle.hitbox{
r:5px;
stroke:none;
stroke-width:0;
}
.lbead{
fill: var(--nord12);
r: 2.65px;
}
.hitbox{
opacity:0;
stroke-width: 2;
fill: red;
stroke: red;
}
#coin circle{
r: 2.7px;
fill:darkgray;
}
#coin text{
fill:var(--nord0);
font-size: 4.259px;
line-height:1.25;
font-family: crosses;
}
#cross .symbol{
font-family: crosses;
fill: var(--nord4);
font-size: 17.3637px;
line-height: 1.25;
stroke-width:0.434093
}
table{
width: 100%;
border-collapse: collapse;
}
td{
text-align:center;
border-left: 1px solid;
border-right: 1px solid;
border-color: var(--nord2);
padding-left: 5px;
padding-right: 5px;
}
tr :last-child{
border-right: none;
}
tr :first-child{
border-left: 0px solid;
}
thead td{
color: var(--nord4);
border-bottom-width: 3px;
border-bottom-color: var(--nord10);
border-bottom-style: dotted;
font-size: 110%;
font-weight: bold;
}
.table{
width:100%;
overflow-x: auto;
}

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import "$lib/css/nordtheme.css";
import LinksGrid from "$lib/components/LinksGrid.svelte"; import LinksGrid from "$lib/components/LinksGrid.svelte";
import { onMount } from 'svelte'; import { onMount } from 'svelte';
let { data } = $props(); let { data } = $props();

View File

@@ -1,6 +1,40 @@
<script> <style>
import "$lib/css/form.css" form {
</script> background-color: var(--color-bg-secondary);
display: flex;
flex-direction: column;
max-width: 600px;
gap: 0.5em;
margin-inline: auto;
justify-content: center;
align-items: center;
padding-block: 2rem;
margin-block: 2rem;
}
form label { font-size: 1.2em; }
form input { display: block; font-size: 1.2rem; }
form button {
background-color: var(--color-accent);
color: var(--color-text-on-accent);
border: none;
padding: 0.5em 1em;
font-size: 1.3em;
border-radius: 1000px;
margin-top: 1em;
transition: 100ms;
cursor: pointer;
}
form button:hover, form button:focus-visible {
background-color: var(--color-accent-hover);
scale: 1.1;
}
form button:active { background-color: var(--color-accent-active); }
form p { max-width: 400px; margin-top: 0; }
form h4 { margin-bottom: 0; }
@media screen and (max-width: 600px) {
form { margin-top: 0; }
}
</style>
<form action="?/register" method=POST> <form action="?/register" method=POST>
<h1>Registrieren</h1> <h1>Registrieren</h1>

View File

@@ -3,9 +3,44 @@
export let data export let data
let password; let password;
const admin = data.user?.access.includes('admin') ?? false const admin = data.user?.access.includes('admin') ?? false
import "$lib/css/form.css"
</script> </script>
<style> <style>
form {
background-color: var(--color-bg-secondary);
display: flex;
flex-direction: column;
max-width: 600px;
gap: 0.5em;
margin-inline: auto;
justify-content: center;
align-items: center;
padding-block: 2rem;
margin-block: 2rem;
}
form label { font-size: 1.2em; }
form input { display: block; font-size: 1.2rem; }
form button {
background-color: var(--color-accent);
color: var(--color-text-on-accent);
border: none;
padding: 0.5em 1em;
font-size: 1.3em;
border-radius: 1000px;
margin-top: 1em;
transition: 100ms;
cursor: pointer;
}
form button:hover, form button:focus-visible {
background-color: var(--color-accent-hover);
scale: 1.1;
}
form button:active { background-color: var(--color-accent-active); }
form p { max-width: 400px; margin-top: 0; }
form h4 { margin-bottom: 0; }
@media screen and (max-width: 600px) {
form { margin-top: 0; }
}
input:invalid + div{ input:invalid + div{
display: none; display: none;
} }

View File

@@ -1,4 +1,5 @@
<script> <script>
import '../app.css';
let { children } = $props(); let { children } = $props();
</script> </script>

View File

@@ -1,4 +1,5 @@
<script> <script>
import '$lib/css/christ.css';
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';

View File

@@ -2,8 +2,6 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import { createLanguageContext } from "$lib/contexts/languageContext.js"; import { createLanguageContext } from "$lib/contexts/languageContext.js";
import "$lib/css/christ.css";
import "$lib/css/nordtheme.css";
import Gebet from "./Gebet.svelte"; import Gebet from "./Gebet.svelte";
import LanguageToggle from "$lib/components/faith/LanguageToggle.svelte"; import LanguageToggle from "$lib/components/faith/LanguageToggle.svelte";
import SearchInput from "$lib/components/SearchInput.svelte"; import SearchInput from "$lib/components/SearchInput.svelte";

View File

@@ -1,8 +1,6 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { createLanguageContext } from "$lib/contexts/languageContext.js"; import { createLanguageContext } from "$lib/contexts/languageContext.js";
import "$lib/css/christ.css";
import "$lib/css/nordtheme.css";
import LanguageToggle from "$lib/components/faith/LanguageToggle.svelte"; import LanguageToggle from "$lib/components/faith/LanguageToggle.svelte";
import Kreuzzeichen from "$lib/components/faith/prayers/Kreuzzeichen.svelte"; import Kreuzzeichen from "$lib/components/faith/prayers/Kreuzzeichen.svelte";
import GloriaPatri from "$lib/components/faith/prayers/GloriaPatri.svelte"; import GloriaPatri from "$lib/components/faith/prayers/GloriaPatri.svelte";

View File

@@ -3,7 +3,6 @@ import { onMount, tick } from "svelte";
import { createLanguageContext } from "$lib/contexts/languageContext.js"; import { createLanguageContext } from "$lib/contexts/languageContext.js";
import { createPip } from "$lib/js/pip.svelte"; import { createPip } from "$lib/js/pip.svelte";
import PipImage from "$lib/components/faith/PipImage.svelte"; import PipImage from "$lib/components/faith/PipImage.svelte";
import "$lib/css/christ.css";
import "$lib/css/action_button.css"; import "$lib/css/action_button.css";
import Kreuzzeichen from "$lib/components/faith/prayers/Kreuzzeichen.svelte"; import Kreuzzeichen from "$lib/components/faith/prayers/Kreuzzeichen.svelte";
import Credo from "$lib/components/faith/prayers/Credo.svelte"; import Credo from "$lib/components/faith/prayers/Credo.svelte";
@@ -75,6 +74,7 @@ let currentMysteryDescriptions = $derived(data.mysteryDescriptions[selectedMyste
// Function to switch mysteries // Function to switch mysteries
function selectMystery(mysteryType) { function selectMystery(mysteryType) {
selectedMystery = mysteryType; selectedMystery = mysteryType;
lastMysteryTarget = 'before';
} }
// Build URLs preserving full state (for no-JS fallback) // Build URLs preserving full state (for no-JS fallback)
@@ -163,6 +163,7 @@ $effect(() => {
let mysteryImageContainer; let mysteryImageContainer;
let mysteryScrollRaf = null; let mysteryScrollRaf = null;
let lastMysteryTarget = 'before';
function scrollMysteryImage(targetY, duration = 1200) { function scrollMysteryImage(targetY, duration = 1200) {
if (!mysteryImageContainer) return; if (!mysteryImageContainer) return;
@@ -193,14 +194,17 @@ $effect(() => {
// Edge pads (before/after): scroll flush so previous image hides behind the header // Edge pads (before/after): scroll flush so previous image hides behind the header
const offset = isEdge ? 0 : rem * IMAGE_COL_HEADER_OFFSET; const offset = isEdge ? 0 : rem * IMAGE_COL_HEADER_OFFSET;
const target = Math.max(0, targetEl.offsetTop - offset); const target = Math.max(0, targetEl.offsetTop - offset);
if (isEdge) { // Smooth-scroll the last image away when naturally transitioning from decade 5 to 'after'
// Snap instantly when jumping to top/bottom const isNaturalEnd = targetName === 'after' && lastMysteryTarget === 5;
if (isEdge && !isNaturalEnd) {
// Snap instantly when jumping to top/bottom (not a natural scroll)
if (mysteryScrollRaf) cancelAnimationFrame(mysteryScrollRaf); if (mysteryScrollRaf) cancelAnimationFrame(mysteryScrollRaf);
mysteryScrollRaf = null; mysteryScrollRaf = null;
mysteryImageContainer.scrollTop = target; mysteryImageContainer.scrollTop = target;
} else { } else {
scrollMysteryImage(target); scrollMysteryImage(target);
} }
lastMysteryTarget = targetName;
} }
}); });

View File

@@ -1,4 +1,5 @@
<script> <script>
import '$lib/css/recipe-links.css';
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';

View File

@@ -3,7 +3,6 @@
export const multiplier = writable(0); export const multiplier = writable(0);
import type { PageData } from './$types'; import type { PageData } from './$types';
import "$lib/css/nordtheme.css"
import EditButton from '$lib/components/EditButton.svelte'; import EditButton from '$lib/components/EditButton.svelte';
import InstructionsPage from '$lib/components/recipes/InstructionsPage.svelte'; import InstructionsPage from '$lib/components/recipes/InstructionsPage.svelte';
import IngredientsPage from '$lib/components/recipes/IngredientsPage.svelte'; import IngredientsPage from '$lib/components/recipes/IngredientsPage.svelte';

View File

@@ -10,7 +10,6 @@
import CreateStepList from '$lib/components/recipes/CreateStepList.svelte'; import CreateStepList from '$lib/components/recipes/CreateStepList.svelte';
import Toggle from '$lib/components/Toggle.svelte'; import Toggle from '$lib/components/Toggle.svelte';
import '$lib/css/action_button.css'; import '$lib/css/action_button.css';
import '$lib/css/nordtheme.css';
let { data, form }: { data: PageData; form: ActionData } = $props(); let { data, form }: { data: PageData; form: ActionData } = $props();

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types'; import type { PageData } from './$types';
import '$lib/css/nordtheme.css';
import Recipes from '$lib/components/recipes/Recipes.svelte'; import Recipes from '$lib/components/recipes/Recipes.svelte';
import Card from '$lib/components/recipes/Card.svelte'; import Card from '$lib/components/recipes/Card.svelte';

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types'; import type { PageData } from './$types';
import '$lib/css/nordtheme.css';
let { data } = $props<{ data: PageData }>(); let { data } = $props<{ data: PageData }>();

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types'; import type { PageData } from './$types';
import "$lib/css/nordtheme.css";
let { data } = $props<{ data: PageData }>(); let { data } = $props<{ data: PageData }>();
import TagCloud from '$lib/components/TagCloud.svelte'; import TagCloud from '$lib/components/TagCloud.svelte';
import TagBall from '$lib/components/TagBall.svelte'; import TagBall from '$lib/components/TagBall.svelte';

View File

@@ -15,7 +15,6 @@
import { season } from '$lib/js/season_store'; import { season } from '$lib/js/season_store';
import { portions } from '$lib/js/portions_store'; import { portions } from '$lib/js/portions_store';
import '$lib/css/action_button.css'; import '$lib/css/action_button.css';
import '$lib/css/nordtheme.css';
let { data, form }: { data: PageData; form: ActionData } = $props(); let { data, form }: { data: PageData; form: ActionData } = $props();

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types'; import type { PageData } from './$types';
import '$lib/css/nordtheme.css';
import Recipes from '$lib/components/recipes/Recipes.svelte'; import Recipes from '$lib/components/recipes/Recipes.svelte';
import Card from '$lib/components/recipes/Card.svelte'; import Card from '$lib/components/recipes/Card.svelte';
import Search from '$lib/components/recipes/Search.svelte'; import Search from '$lib/components/recipes/Search.svelte';

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types'; import type { PageData } from './$types';
import '$lib/css/nordtheme.css';
import Recipes from '$lib/components/recipes/Recipes.svelte'; import Recipes from '$lib/components/recipes/Recipes.svelte';
import MediaScroller from '$lib/components/recipes/MediaScroller.svelte'; import MediaScroller from '$lib/components/recipes/MediaScroller.svelte';
import SeasonLayout from '$lib/components/recipes/SeasonLayout.svelte' import SeasonLayout from '$lib/components/recipes/SeasonLayout.svelte'

View File

@@ -1,6 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types'; import type { PageData } from './$types';
import '$lib/css/nordtheme.css';
import Recipes from '$lib/components/recipes/Recipes.svelte'; import Recipes from '$lib/components/recipes/Recipes.svelte';
import MediaScroller from '$lib/components/recipes/MediaScroller.svelte'; import MediaScroller from '$lib/components/recipes/MediaScroller.svelte';
import SeasonLayout from '$lib/components/recipes/SeasonLayout.svelte' import SeasonLayout from '$lib/components/recipes/SeasonLayout.svelte'

View File

@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types'; import type { PageData } from './$types';
let { data } = $props<{ data: PageData }>(); let { data } = $props<{ data: PageData }>();
import "$lib/css/nordtheme.css";
import TagCloud from '$lib/components/TagCloud.svelte'; import TagCloud from '$lib/components/TagCloud.svelte';
import TagBall from '$lib/components/TagBall.svelte'; import TagBall from '$lib/components/TagBall.svelte';