diff --git a/src/widgets/ComparisonView/ui/FontList/FontList.svelte b/src/widgets/ComparisonView/ui/FontList/FontList.svelte
index e8ae2b9..5b4b455 100644
--- a/src/widgets/ComparisonView/ui/FontList/FontList.svelte
+++ b/src/widgets/ComparisonView/ui/FontList/FontList.svelte
@@ -9,6 +9,7 @@ import {
FontVirtualList,
type UnifiedFont,
VIRTUAL_INDEX_NOT_LOADED,
+ appliedFontsManager,
fontStore,
} from '$entities/Font';
import { getSkeletonWidth } from '$shared/lib/utils';
@@ -18,6 +19,7 @@ import {
Skeleton,
} from '$shared/ui';
import DotIcon from '@lucide/svelte/icons/dot';
+import { fade } from 'svelte/transition';
import {
createDotCrossfade,
getDotTransitionParams,
@@ -71,6 +73,21 @@ function handleSelect(font: UnifiedFont) {
comparisonStore.fontB = font;
}
}
+
+/**
+ * Returns true once the font file is loaded (or errored) and safe to render.
+ * Called inside the template — Svelte 5 tracks the $state reads inside
+ * appliedFontsManager.getFontStatus(), so each row re-renders reactively
+ * when its file arrives.
+ */
+function isFontReady(font: UnifiedFont): boolean {
+ const status = appliedFontsManager.getFontStatus(
+ font.id,
+ DEFAULT_FONT_WEIGHT,
+ font.features?.isVariable,
+ );
+ return status === 'loaded' || status === 'error';
+}
@@ -83,77 +100,93 @@ function handleSelect(font: UnifiedFont) {
{#snippet skeleton()}
- {#each { length: 12 } as _, i (i)}
-
-
-
+
+ {#each { length: 50 } as _, index (index)}
+
-
-
- {/each}
+ {/each}
+
{/snippet}
{#snippet children({ item: font, index })}
- {@const isSelectedA = font.id === comparisonStore.fontA?.id}
- {@const isSelectedB = font.id === comparisonStore.fontB?.id}
- {@const active = (side === 'A' && isSelectedA) || (side === 'B' && isSelectedB)}
+
+ {#if !isFontReady(font)}
+
+
+
+
+ {:else}
+ {@const isSelectedA = font.id === comparisonStore.fontA?.id}
+ {@const isSelectedB = font.id === comparisonStore.fontB?.id}
+ {@const active = (side === 'A' && isSelectedA) || (side === 'B' && isSelectedB)}
-
{/snippet}