All checks were successful
CI / update (push) Successful in 1m13s
- Add aria-labels to icon-only links (add button, edit button, logo, nav toggle) - Add main landmark element for better page structure - Fix heading hierarchy on recipe pages (h1 → h2 → h3 progression) - Add role="status" to loading placeholders to allow aria-label usage - Update link colors from red to blue for better contrast in both light and dark modes - Change hover colors from orange/red to light blue across all interactive elements - Reduce font size of section labels (Season, Keywords) while maintaining semantic structure These changes address PageSpeed accessibility recommendations including low-contrast text, missing accessible names, prohibited ARIA attributes, missing landmarks, and improper heading order.
67 lines
1.5 KiB
Svelte
67 lines
1.5 KiB
Svelte
<script>
|
|
import { onMount } from 'svelte';
|
|
import { browser } from '$app/environment';
|
|
|
|
let {
|
|
title = '',
|
|
eager = false,
|
|
estimatedHeight = 400,
|
|
rootMargin = '400px',
|
|
children
|
|
} = $props();
|
|
|
|
let isVisible = $state(eager); // If eager=true, render immediately
|
|
let containerRef = $state(null);
|
|
let observer = $state(null);
|
|
|
|
onMount(() => {
|
|
if (!browser || eager) return;
|
|
|
|
// Create Intersection Observer to detect when category approaches viewport
|
|
observer = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting && !isVisible) {
|
|
isVisible = true;
|
|
// Once visible, stop observing (keep it rendered)
|
|
if (observer && containerRef) {
|
|
observer.unobserve(containerRef);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
{
|
|
rootMargin, // Start loading 400px before entering viewport
|
|
threshold: 0
|
|
}
|
|
);
|
|
|
|
if (containerRef) {
|
|
observer.observe(containerRef);
|
|
}
|
|
|
|
return () => {
|
|
if (observer) {
|
|
observer.disconnect();
|
|
}
|
|
};
|
|
});
|
|
</script>
|
|
|
|
{#if isVisible}
|
|
<!-- Render actual content when visible -->
|
|
<div bind:this={containerRef}>
|
|
{@render children()}
|
|
</div>
|
|
{:else}
|
|
<!-- Placeholder with estimated height to maintain scroll position -->
|
|
<div
|
|
bind:this={containerRef}
|
|
style="height: {estimatedHeight}px; min-height: {estimatedHeight}px;"
|
|
role="status"
|
|
aria-label="Loading {title}"
|
|
>
|
|
<!-- Empty placeholder - IntersectionObserver will trigger when this enters viewport -->
|
|
</div>
|
|
{/if}
|