79 lines
1.9 KiB
Svelte
79 lines
1.9 KiB
Svelte
<!--
|
|
Component: FontApplicator
|
|
Loads fonts from fontshare with link tag
|
|
- Loads font only if it's not already applied
|
|
- Reacts to font load status to show/hide content
|
|
- Adds smooth transition when font appears
|
|
-->
|
|
<script lang="ts">
|
|
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
|
import type { Snippet } from 'svelte';
|
|
import { prefersReducedMotion } from 'svelte/motion';
|
|
import {
|
|
type UnifiedFont,
|
|
appliedFontsManager,
|
|
} from '../../model';
|
|
|
|
interface Props {
|
|
/**
|
|
* Font to apply
|
|
*/
|
|
font: UnifiedFont;
|
|
/**
|
|
* Font weight
|
|
* @default 400
|
|
*/
|
|
weight?: number;
|
|
/**
|
|
* CSS classes
|
|
*/
|
|
className?: string;
|
|
/**
|
|
* Content snippet
|
|
*/
|
|
children?: Snippet;
|
|
}
|
|
|
|
let {
|
|
font,
|
|
weight = 400,
|
|
className,
|
|
children,
|
|
}: Props = $props();
|
|
|
|
const status = $derived(
|
|
appliedFontsManager.getFontStatus(
|
|
font.id,
|
|
weight,
|
|
font.features?.isVariable,
|
|
),
|
|
);
|
|
|
|
// 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 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>
|
|
|
|
<div
|
|
style:font-family={shouldReveal
|
|
? `'${font.name}'`
|
|
: 'system-ui, -apple-system, sans-serif'}
|
|
class={cn(
|
|
transitionClasses,
|
|
// If reduced motion is on, we skip the transform/blur entirely
|
|
!shouldReveal
|
|
&& !prefersReducedMotion.current
|
|
&& 'opacity-50 scale-[0.95] blur-sm',
|
|
!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>
|