diff --git a/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts b/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts index 787cca9..d277e5f 100644 --- a/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts +++ b/src/widgets/ComparisonSlider/model/stores/comparisonStore.svelte.ts @@ -34,6 +34,7 @@ class ComparisonStore { #fontB = $state(); #sampleText = $state('The quick brown fox jumps over the lazy dog'); #isRestoring = $state(true); + #fontsReady = $state(false); #typography = createTypographyControlManager(DEFAULT_TYPOGRAPHY_CONTROLS_DATA, 'glyphdiff:comparison:typography'); constructor() { @@ -49,6 +50,7 @@ class ComparisonStore { // If we already have a selection, do nothing if (this.#fontA && this.#fontB) { + this.#checkFontsLoaded(); return; } @@ -66,6 +68,48 @@ class ComparisonStore { }); } + /** + * Checks if fonts are actually loaded in the browser at current weight. + * Uses CSS Font Loading API to prevent FOUT. + */ + async #checkFontsLoaded() { + if (!('fonts' in document)) { + this.#fontsReady = true; + return; + } + + this.#fontsReady = false; + + const weight = this.#typography.weight; + const size = this.#typography.renderedSize; + const fontAName = this.#fontA?.name; + const fontBName = this.#fontB?.name; + + if (!fontAName || !fontBName) return; + + try { + // Step 1: Load fonts into memory + await Promise.all([ + document.fonts.load(`${weight} ${size}px "${fontAName}"`), + document.fonts.load(`${weight} ${size}px "${fontBName}"`), + ]); + + // Step 2: Wait for browser to be ready to render + await document.fonts.ready; + + // Step 3: Force a layout/paint cycle (critical!) + await new Promise(resolve => { + requestAnimationFrame(() => { + requestAnimationFrame(resolve); // Double rAF ensures paint completes + }); + }); + + this.#fontsReady = true; + } catch (error) { + console.warn('[ComparisonStore] Font loading failed:', error); + setTimeout(() => this.#fontsReady = true, 1000); + } + } /** * Restore state from persistent storage */ @@ -141,13 +185,12 @@ class ComparisonStore { * Check if both fonts are selected */ get isReady() { - return !!this.#fontA && !!this.#fontB; + return !!this.#fontA && !!this.#fontB && this.#fontsReady; } get isLoading() { - return this.#isRestoring; + return this.#isRestoring || !this.#fontsReady; } - /** * Public initializer (optional, as constructor starts it) * Kept for compatibility if manual re-init is needed