Files
frontend-svelte/src/entities/Font/ui/FontApplicator/FontApplicator.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>