From 7bec5e5b6ad518fa4a7106f23be081f3b454dd8e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 3 Jul 2025 19:34:53 +0000 Subject: [PATCH 1/3] Prevent FOUC during client hints reload with improved stop mechanism Co-authored-by: me --- FOUC_SOLUTION.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 13 +++++++++- 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 FOUC_SOLUTION.md diff --git a/FOUC_SOLUTION.md b/FOUC_SOLUTION.md new file mode 100644 index 0000000..8091776 --- /dev/null +++ b/FOUC_SOLUTION.md @@ -0,0 +1,66 @@ +# Flash of Unstyled Content (FOUC) Solution + +## Problem Analysis + +The issue was in the client hints library's reload mechanism. When client preferences (like color scheme, timezone, reduced motion) changed, the system would trigger a page reload using `window.location.reload()`. However, this simple approach caused a **Flash of Unstyled Content (FOUC)** because: + +1. The reload was triggered but the browser continued processing the current HTML document +2. Resource requests on the page got canceled during the reload process +3. This led to partially rendered content without styles before the reload completed + +## Root Cause + +The problematic code was in `src/index.ts` at line 76-80: + +```javascript +if (cookieChanged) window.location.reload(); +``` + +This single line allowed the browser to continue rendering the current page state while initiating the reload, causing the visual flash. + +## Solution Implemented + +The solution involves a three-step approach to prevent any visual artifacts: + +```javascript +if (cookieChanged) { + // Stop all resource loading and DOM processing to prevent FOUC + if (window.stop) window.stop(); + + // Hide the page content immediately to prevent visual flicker + const style = document.createElement('style'); + style.textContent = 'html { visibility: hidden !important; }'; + document.head.appendChild(style); + + // Trigger the reload + window.location.reload(); +} +``` + +### Step-by-Step Breakdown + +1. **Stop Processing**: `window.stop()` immediately halts all current resource loading and DOM processing +2. **Hide Content**: Inject a style tag that makes the entire HTML document invisible using `visibility: hidden !important` +3. **Reload**: Only then trigger the actual page reload + +## Benefits of This Approach + +- **Eliminates FOUC**: No flash of unstyled content during the transition +- **Backward Compatible**: All existing tests pass without modification +- **Graceful Degradation**: The `if (window.stop)` check ensures compatibility with browsers that might not support this method +- **Immediate Effect**: The visibility hidden style is applied synchronously, preventing any visual artifacts + +## Testing & Validation + +- ✅ All existing unit tests pass +- ✅ TypeScript compilation succeeds without errors +- ✅ Solution is included in the compiled JavaScript output +- ✅ Maintains the same core functionality while solving the visual issue + +## Browser Compatibility + +- `window.stop()` is supported in all modern browsers +- The `visibility: hidden` CSS property is universally supported +- The solution gracefully handles edge cases where `window.stop()` might not be available + +This solution ensures a smooth user experience during client hint changes while maintaining the library's existing functionality and API. \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index ddb6d80..70e0eb2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -83,7 +83,18 @@ function checkClientHints() { cookieChanged = true; } } - if (cookieChanged) window.location.reload(); + if (cookieChanged) { + // Stop all resource loading and DOM processing to prevent FOUC + if (window.stop) window.stop(); + + // Hide the page content immediately to prevent visual flicker + const style = document.createElement('style'); + style.textContent = 'html { visibility: hidden !important; }'; + document.head.appendChild(style); + + // Trigger the reload + window.location.reload(); + } } checkClientHints(); From 545c8ac48d205231be19d44903a596465a65de78 Mon Sep 17 00:00:00 2001 From: "Kent C. Dodds" Date: Thu, 3 Jul 2025 13:41:24 -0600 Subject: [PATCH 2/3] Delete FOUC_SOLUTION.md --- FOUC_SOLUTION.md | 66 ------------------------------------------------ 1 file changed, 66 deletions(-) delete mode 100644 FOUC_SOLUTION.md diff --git a/FOUC_SOLUTION.md b/FOUC_SOLUTION.md deleted file mode 100644 index 8091776..0000000 --- a/FOUC_SOLUTION.md +++ /dev/null @@ -1,66 +0,0 @@ -# Flash of Unstyled Content (FOUC) Solution - -## Problem Analysis - -The issue was in the client hints library's reload mechanism. When client preferences (like color scheme, timezone, reduced motion) changed, the system would trigger a page reload using `window.location.reload()`. However, this simple approach caused a **Flash of Unstyled Content (FOUC)** because: - -1. The reload was triggered but the browser continued processing the current HTML document -2. Resource requests on the page got canceled during the reload process -3. This led to partially rendered content without styles before the reload completed - -## Root Cause - -The problematic code was in `src/index.ts` at line 76-80: - -```javascript -if (cookieChanged) window.location.reload(); -``` - -This single line allowed the browser to continue rendering the current page state while initiating the reload, causing the visual flash. - -## Solution Implemented - -The solution involves a three-step approach to prevent any visual artifacts: - -```javascript -if (cookieChanged) { - // Stop all resource loading and DOM processing to prevent FOUC - if (window.stop) window.stop(); - - // Hide the page content immediately to prevent visual flicker - const style = document.createElement('style'); - style.textContent = 'html { visibility: hidden !important; }'; - document.head.appendChild(style); - - // Trigger the reload - window.location.reload(); -} -``` - -### Step-by-Step Breakdown - -1. **Stop Processing**: `window.stop()` immediately halts all current resource loading and DOM processing -2. **Hide Content**: Inject a style tag that makes the entire HTML document invisible using `visibility: hidden !important` -3. **Reload**: Only then trigger the actual page reload - -## Benefits of This Approach - -- **Eliminates FOUC**: No flash of unstyled content during the transition -- **Backward Compatible**: All existing tests pass without modification -- **Graceful Degradation**: The `if (window.stop)` check ensures compatibility with browsers that might not support this method -- **Immediate Effect**: The visibility hidden style is applied synchronously, preventing any visual artifacts - -## Testing & Validation - -- ✅ All existing unit tests pass -- ✅ TypeScript compilation succeeds without errors -- ✅ Solution is included in the compiled JavaScript output -- ✅ Maintains the same core functionality while solving the visual issue - -## Browser Compatibility - -- `window.stop()` is supported in all modern browsers -- The `visibility: hidden` CSS property is universally supported -- The solution gracefully handles edge cases where `window.stop()` might not be available - -This solution ensures a smooth user experience during client hint changes while maintaining the library's existing functionality and API. \ No newline at end of file From d6478c1b547d330ec551670b184fa51d954d2ace Mon Sep 17 00:00:00 2001 From: "Kent C. Dodds" Date: Thu, 3 Jul 2025 13:41:51 -0600 Subject: [PATCH 3/3] Update index.ts --- src/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index 70e0eb2..1b07c1f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -84,14 +84,11 @@ function checkClientHints() { } } if (cookieChanged) { - // Stop all resource loading and DOM processing to prevent FOUC - if (window.stop) window.stop(); - // Hide the page content immediately to prevent visual flicker const style = document.createElement('style'); style.textContent = 'html { visibility: hidden !important; }'; document.head.appendChild(style); - + // Trigger the reload window.location.reload(); }