experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(page): dark-mode-aware loading background with fade-in to prevent bright flash

The loading state hardcoded rgba(255,255,255,0.95) regardless of color
scheme, causing a bright white flash before pages load in dark mode.
Added prefers-color-scheme: dark override with rgba(30,30,30,0.95) and
restored bg-fade-in animation (0.3s from transparent) for both modes.
Includes regression tests for light/dark backgrounds and fade-in.

+107 -1
+19 -1
app/page/index.html
··· 59 59 } 60 60 } 61 61 62 + /* Fade background in from transparent to avoid bright flash */ 63 + @keyframes bg-fade-in { 64 + from { background-color: transparent; } 65 + to { background-color: rgba(255, 255, 255, 0.95); } 66 + } 67 + 68 + @keyframes bg-fade-in-dark { 69 + from { background-color: transparent; } 70 + to { background-color: rgba(30, 30, 30, 0.95); } 71 + } 72 + 62 73 webview.loading { 63 74 opacity: 1; 64 75 background-color: rgba(255, 255, 255, 0.95); 65 - animation: loading-glow 1.2s ease-in-out infinite; 76 + animation: bg-fade-in 0.3s ease forwards, loading-glow 1.2s ease-in-out 0.3s infinite; 66 77 overflow: visible; 78 + } 79 + 80 + @media (prefers-color-scheme: dark) { 81 + webview.loading { 82 + background-color: rgba(30, 30, 30, 0.95); 83 + animation: bg-fade-in-dark 0.3s ease forwards, loading-glow 1.2s ease-in-out 0.3s infinite; 84 + } 67 85 } 68 86 69 87 /* Default visible state (after loading completes) */
+88
backend/electron/page-loading.test.ts
··· 1 + import { describe, it } from 'node:test'; 2 + import { strict as assert } from 'node:assert'; 3 + import { readFileSync } from 'fs'; 4 + import { join, dirname } from 'path'; 5 + import { fileURLToPath } from 'url'; 6 + 7 + const __dirname = dirname(fileURLToPath(import.meta.url)); 8 + // When compiled, __dirname is dist/backend/electron/ — go up 3 levels to project root 9 + const pageHtml = readFileSync(join(__dirname, '..', '..', '..', 'app', 'page', 'index.html'), 'utf-8'); 10 + 11 + describe('Page loading CSS', () => { 12 + it('webview.loading should have a dark mode background', () => { 13 + // The loading state must not hardcode a bright white background. 14 + // Without dark mode handling, users see a bright flash before pages load. 15 + assert.ok( 16 + pageHtml.includes('prefers-color-scheme: dark'), 17 + 'page/index.html must include a prefers-color-scheme: dark media query' 18 + ); 19 + 20 + // The dark media query should contain a loading background override 21 + const darkBlock = pageHtml.match( 22 + /@media\s*\(prefers-color-scheme:\s*dark\)\s*\{([^}]*webview\.loading[^}]*)\}/s 23 + ); 24 + assert.ok( 25 + darkBlock, 26 + 'Dark mode media query must contain a webview.loading rule' 27 + ); 28 + assert.ok( 29 + darkBlock[1].includes('background-color'), 30 + 'Dark mode webview.loading must set background-color' 31 + ); 32 + 33 + // The dark background should not be white/bright 34 + const bgMatch = darkBlock[1].match(/background-color:\s*([^;]+);/); 35 + assert.ok(bgMatch, 'Dark mode background-color must have a value'); 36 + const bgValue = bgMatch[1].trim(); 37 + assert.ok( 38 + !bgValue.includes('255, 255, 255'), 39 + `Dark mode loading background must not be white, got: ${bgValue}` 40 + ); 41 + }); 42 + 43 + it('webview.loading should fade in from transparent', () => { 44 + // The loading state must not snap to an opaque background instantly. 45 + // A fade-in animation prevents the bright flash before content loads. 46 + assert.ok( 47 + pageHtml.includes('bg-fade-in'), 48 + 'page/index.html must include a bg-fade-in animation' 49 + ); 50 + 51 + // The loading class should use the fade-in animation 52 + const loadingBlock = pageHtml.match( 53 + /webview\.loading\s*\{([^}]+)\}/ 54 + ); 55 + assert.ok(loadingBlock, 'webview.loading rule must exist'); 56 + assert.ok( 57 + loadingBlock[1].includes('bg-fade-in'), 58 + 'webview.loading must reference bg-fade-in animation' 59 + ); 60 + 61 + // The fade-in keyframes should start from transparent 62 + const fadeKeyframes = pageHtml.match( 63 + /@keyframes\s+bg-fade-in\s*\{([^}]*from[^}]*)\}/s 64 + ); 65 + assert.ok(fadeKeyframes, 'bg-fade-in keyframes must exist'); 66 + assert.ok( 67 + fadeKeyframes[1].includes('transparent'), 68 + 'bg-fade-in must start from transparent' 69 + ); 70 + }); 71 + 72 + it('light mode loading background should be light', () => { 73 + // The default (light mode) loading background should be a light color 74 + const loadingBlock = pageHtml.match( 75 + /webview\.loading\s*\{([^}]+)\}/ 76 + ); 77 + assert.ok(loadingBlock, 'webview.loading rule must exist'); 78 + 79 + const bgMatch = loadingBlock[1].match(/background-color:\s*([^;]+);/); 80 + assert.ok(bgMatch, 'webview.loading must set background-color'); 81 + const bgValue = bgMatch[1].trim(); 82 + // Should contain white-ish values (255, 255, 255) for light mode 83 + assert.ok( 84 + bgValue.includes('255') || bgValue.includes('#fff') || bgValue.includes('#FFF'), 85 + `Light mode loading background should be light, got: ${bgValue}` 86 + ); 87 + }); 88 + });