feat(FontApplicator): add skeleton snippet prop to replace blur loading state
This commit is contained in:
@@ -1,14 +1,11 @@
|
|||||||
<!--
|
<!--
|
||||||
Component: FontApplicator
|
Component: FontApplicator
|
||||||
Loads fonts from fontshare with link tag
|
Applies a font to its children once the font file is loaded.
|
||||||
- Loads font only if it's not already applied
|
Shows the skeleton snippet while loading; falls back to system font if no skeleton is provided.
|
||||||
- Reacts to font load status to show/hide content
|
|
||||||
- Adds smooth transition when font appears
|
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
import { prefersReducedMotion } from 'svelte/motion';
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_FONT_WEIGHT,
|
DEFAULT_FONT_WEIGHT,
|
||||||
type UnifiedFont,
|
type UnifiedFont,
|
||||||
@@ -33,6 +30,11 @@ interface Props {
|
|||||||
* Content snippet
|
* Content snippet
|
||||||
*/
|
*/
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
|
/**
|
||||||
|
* Shown while the font file is loading.
|
||||||
|
* When omitted, children render in system font until ready.
|
||||||
|
*/
|
||||||
|
skeleton?: Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@@ -40,6 +42,7 @@ let {
|
|||||||
weight = DEFAULT_FONT_WEIGHT,
|
weight = DEFAULT_FONT_WEIGHT,
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
|
skeleton,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const status = $derived(
|
const status = $derived(
|
||||||
@@ -50,30 +53,16 @@ const status = $derived(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// The "Show" condition: Font is loaded OR it errored out OR it's a noTouch preview (like in search)
|
|
||||||
const shouldReveal = $derived(status === 'loaded' || status === 'error');
|
const shouldReveal = $derived(status === 'loaded' || status === 'error');
|
||||||
|
|
||||||
const transitionClasses = $derived(
|
|
||||||
prefersReducedMotion.current
|
|
||||||
? 'transition-none' // Disable CSS transitions if motion is reduced
|
|
||||||
: 'transition-all duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]',
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
{#if !shouldReveal && skeleton}
|
||||||
style:font-family={shouldReveal
|
{@render skeleton()}
|
||||||
? `'${font.name}'`
|
{:else}
|
||||||
: 'system-ui, -apple-system, sans-serif'}
|
<div
|
||||||
class={clsx(
|
style:font-family={shouldReveal ? `'${font.name}'` : 'system-ui, -apple-system, sans-serif'}
|
||||||
transitionClasses,
|
class={clsx(className)}
|
||||||
// If reduced motion is on, we skip the transform/blur entirely
|
>
|
||||||
!shouldReveal
|
{@render children?.()}
|
||||||
&& !prefersReducedMotion.current
|
</div>
|
||||||
&& 'opacity-50 scale-[0.95] blur-sm',
|
{/if}
|
||||||
!shouldReveal && prefersReducedMotion.current && 'opacity-0', // Still hide until font is ready, but no movement
|
|
||||||
shouldReveal && 'opacity-100 scale-100 blur-0',
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user