faith: progressive enhancement for all faith pages without JS
All checks were successful
CI / update (push) Successful in 1m29s
All checks were successful
CI / update (push) Successful in 1m29s
- Rosary: mystery selection, luminous toggle, and latin toggle fall back to URL params (?mystery=, ?luminous=, ?latin=) for no-JS navigation - Prayers/Angelus: latin toggle uses URL param fallback - Search on prayers page hidden without JS (requires DOM queries) - Toggle component supports href prop for link-based no-JS self-submit - LanguageSelector uses <a> links with computed paths and :focus-within dropdown for no-JS; displays correct language via server-provided prop - Recipe language links use translated slugs from $page.data - URL params cleaned via replaceState after hydration to avoid clutter
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import { createLanguageContext } from "$lib/contexts/languageContext.js";
|
||||
import "$lib/css/christ.css";
|
||||
@@ -22,7 +23,7 @@
|
||||
let { data } = $props();
|
||||
|
||||
// Create language context for prayer components
|
||||
const langContext = createLanguageContext({ urlLang: data.lang });
|
||||
const langContext = createLanguageContext({ urlLang: data.lang, initialLatin: data.initialLatin });
|
||||
|
||||
// Update lang store when data.lang changes (e.g., after navigation)
|
||||
$effect(() => {
|
||||
@@ -57,9 +58,19 @@
|
||||
textMatch: isEnglish ? 'Match in prayer text' : 'Treffer im Gebetstext'
|
||||
});
|
||||
|
||||
// Search state
|
||||
// JS-only search (hidden without JS)
|
||||
let jsEnabled = $state(false);
|
||||
let searchQuery = $state('');
|
||||
|
||||
onMount(() => {
|
||||
jsEnabled = true;
|
||||
|
||||
// Clean up URL params after hydration (state is now in component state)
|
||||
if (window.location.search) {
|
||||
history.replaceState({}, '', window.location.pathname);
|
||||
}
|
||||
});
|
||||
|
||||
// Match results: 'primary' (name/terms), 'secondary' (text only), or null (no match)
|
||||
/** @type {Map<string, 'primary' | 'secondary'>} */
|
||||
let matchResults = $state(/** @type {Map<string, 'primary' | 'secondary'>} */ (new Map()));
|
||||
@@ -165,6 +176,7 @@
|
||||
|
||||
// Helper to get match class for a prayer
|
||||
function getMatchClass(id) {
|
||||
if (!jsEnabled) return '';
|
||||
const match = matchResults.get(id);
|
||||
if (!searchQuery.trim()) return '';
|
||||
if (match === 'primary') return '';
|
||||
@@ -202,6 +214,9 @@
|
||||
joseph: { bilingue: false },
|
||||
confiteor: { bilingue: true }
|
||||
};
|
||||
|
||||
// Toggle href for no-JS fallback (navigates to opposite latin state)
|
||||
const latinToggleHref = $derived(data.initialLatin ? '?latin=0' : '?');
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -264,18 +279,33 @@ h1{
|
||||
color: var(--nord0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Search is hidden without JS */
|
||||
.js-only {
|
||||
display: none;
|
||||
}
|
||||
.js-enabled .js-only {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<div class:js-enabled={jsEnabled}>
|
||||
<h1>{labels.title}</h1>
|
||||
|
||||
<div class="toggle-controls">
|
||||
<LanguageToggle />
|
||||
<LanguageToggle
|
||||
initialLatin={data.initialLatin}
|
||||
hasUrlLatin={data.hasUrlLatin}
|
||||
href={latinToggleHref}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SearchInput
|
||||
bind:value={searchQuery}
|
||||
placeholder={labels.searchPlaceholder}
|
||||
clearTitle={labels.clearSearch}
|
||||
/>
|
||||
<div class="js-only">
|
||||
<SearchInput
|
||||
bind:value={searchQuery}
|
||||
placeholder={labels.searchPlaceholder}
|
||||
clearTitle={labels.clearSearch}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="ccontainer">
|
||||
<div class=container>
|
||||
@@ -317,3 +347,4 @@ h1{
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user