Skip to content

Fade out loading screen and preload wallpaper#173

Draft
itz-rj-here wants to merge 3 commits into
prem-k-r:mainfrom
itz-rj-here:fix-theme-flash
Draft

Fade out loading screen and preload wallpaper#173
itz-rj-here wants to merge 3 commits into
prem-k-r:mainfrom
itz-rj-here:fix-theme-flash

Conversation

@itz-rj-here

@itz-rj-here itz-rj-here commented Apr 5, 2026

Copy link
Copy Markdown
Collaborator

Add smooth fade-out handling for the loading screen and coordinate hiding between theme and wallpaper readiness. Introduce hideLoadingScreen() with transitionend and fallback timeout, and use global flags (_themeReady/_wallpaperReady) so loading screen is only removed once both are ready (with a 3s safety fallback). In wallpaper.js add signalWallpaperReady(), preloadAndApplyBackground() to decode images before applying, and call these from checkAndUpdateImage/applyRandomImage flows; ensure Blob URL cleanup. Add CSS rules for #LoadingScreen fade-out transition and pointer-events handling.## 📌 Description

🎨 Visual Changes (Screenshots / Videos)

🔗 Related Issues

  • Closes #<issue_number>
  • Related to #<issue_number>

✅ Checklist

  • I have read and followed the Contributing Guidelines.
  • My code follows the project's coding style and conventions.
  • I have tested my changes thoroughly to ensure expected behavior.
  • I have verified compatibility across Chrome and Firefox (additional browsers if applicable).
  • I have attached relevant visual evidence (screenshots/videos) if applicable.
  • I have updated the CHANGELOG.md under the appropriate categories with all my changes in this PR.
    Add smooth fade-out handling for the loading screen and coordinate hiding between theme and wallpaper readiness. Introduce hideLoadingScreen() with transitionend and fallback timeout, and use global flags (_themeReady/_wallpaperReady) so loading screen is only removed once both are ready (with a 3s safety fallback). In wallpaper.js add signalWallpaperReady(), preloadAndApplyBackground() to decode images before applying, and call these from checkAndUpdateImage/applyRandomImage flows; ensure Blob URL cleanup. Add CSS rules for #LoadingScreen fade-out transition and pointer-events handling.

Overview

This PR prevents theme and wallpaper flashes on page load by implementing coordinated fade-out of the loading screen and preloading wallpaper images before they are applied. The changes introduce readiness synchronization between theme and wallpaper modules, early dark-mode detection, and image preload/decode workflows.

Key Changes

Loading Screen Synchronization

  • Added hideLoadingScreen() helper in scripts/theme.js that fades out #LoadingScreen by adding a fade-out class with a 0.3s CSS transition, hides the element on transitionend, and includes a 500ms fallback timeout
  • Introduced global readiness flags (window._themeReady and window._wallpaperReady) so the loading screen is only removed once both theme and wallpaper modules signal readiness
  • Added 3-second safety fallback that forces removal if wallpaper readiness is not signaled in time

Wallpaper Preloading

  • Added preloadAndApplyBackground(imageUrl) in scripts/wallpaper.js that decodes images via img.decode() before applying them, with fallback handling for decode failures
  • Introduced signalWallpaperReady() that coordinates with the theme module to conditionally hide the loading screen once both are ready
  • Updated checkAndUpdateImage() control flow to use preload for stored uploads and re-applied random images, with proper Blob URL cleanup

Early Dark-Mode Detection

  • Added dark-mode detection in scripts/preload.js that reads stored theme preference and checks system prefers-color-scheme before initial page paint
  • Conditionally applies early-dark-filter class to document root to prevent light-to-dark flashes during first render
  • Updated #LoadingScreen color variable from #bbd6fd to #000000ff

CSS Enhancements

  • Added html.early-dark-filter rule with filter: invert(1) hue-rotate(180deg) to prevent early flash
  • Updated #LoadingScreen with opacity: 1 and transition: opacity 0.3s ease
  • Added #LoadingScreen.fade-out with opacity: 0 and pointer-events: none for fade-out animation
  • Consolidated dark-mode filter application to consistently use invert(1) hue-rotate(180deg) with webkit prefixes

Storage Persistence

  • Extended toggleBackgroundType in scripts/wallpaper.js to persist selected background type to localStorage as bgType
  • Removes early-dark-filter class when background type is toggled

Technical Details

  • Loading screen removal waits for both _themeReady and _wallpaperReady signals before executing fade-out
  • Image preloading uses decode() method for safe decoding with fallback behavior on failure
  • Three-second safety timeout ensures loading screen removal even if wallpaper module fails to signal readiness
  • Proper cleanup of Blob URLs only in branches where images are explicitly revoked to prevent resource leaks

Add smooth fade-out handling for the loading screen and coordinate hiding between theme and wallpaper readiness. Introduce hideLoadingScreen() with transitionend and fallback timeout, and use global flags (_themeReady/_wallpaperReady) so loading screen is only removed once both are ready (with a 3s safety fallback). In wallpaper.js add signalWallpaperReady(), preloadAndApplyBackground() to decode images before applying, and call these from checkAndUpdateImage/applyRandomImage flows; ensure Blob URL cleanup. Add CSS rules for #LoadingScreen fade-out transition and pointer-events handling.
Add early dark-mode detection in preload.js and a corresponding CSS rule to avoid a light→dark flash on first paint. The preload script now sets the loading screen color default to #000000ff and checks localStorage (preferredTheme, selectedTheme, bgType) plus system preference to decide whether to add an 'early-dark-filter' class to <html>. style.css adds the .early-dark-filter rule (invert + hue-rotate) which is intended to be removed later by wallpaper.js once full DOM selectors and styles are available.
Save the selected background type to localStorage and remove the pre-applied early-dark-filter when toggling wallpapers (scripts/wallpaper.js). Update CSS filter declarations to consistently apply invert+hue-rotate (and -webkit-filter) to page elements including #darkTheme and .favicon to avoid light→dark flash and ensure those elements are inverted where appropriate (style.css).
@coderabbitai

coderabbitai Bot commented Apr 5, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The changes implement an early dark-mode filter system applied during preload to prevent light→dark flashing, introduce a fade-out animation for the loading screen, establish inter-script readiness signaling (window._themeReady and window._wallpaperReady), and persist theme and background preferences in localStorage.

Changes

Cohort / File(s) Summary
Early Dark Mode Filter
scripts/preload.js, style.css
Added preload script that reads theme preferences from localStorage and system prefers-color-scheme, applies early-dark-filter class to root element before paint. Updated CSS with new html.early-dark-filter rule applying invert(1) hue-rotate(180deg) filter and reordered dark-mode filter declarations.
Loading Screen Lifecycle
scripts/theme.js, style.css
Introduced hideLoadingScreen() helper with fade-out animation, replaced unconditional display removal with readiness signaling (window._themeReady), and added 3000ms fallback timeout. Updated CSS with opacity transitions and #LoadingScreen.fade-out class.
Wallpaper Background Management
scripts/wallpaper.js
Added preloadAndApplyBackground() for image preload/decode, introduced signalWallpaperReady() for readiness coordination, updated toggleBackgroundType() to persist bgType to localStorage and remove early-dark-filter, refactored checkAndUpdateImage() control flow to conditionally signal readiness and handle Blob URL revocation per branch.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Browser
    participant Preload as Preload Script
    participant Theme as Theme Script
    participant Wallpaper as Wallpaper Script
    participant DOM as DOM/CSS

    Browser->>Preload: Execute preload.js
    Preload->>Preload: Read theme from localStorage<br/>& prefers-color-scheme
    Preload->>DOM: Apply early-dark-filter class
    Note over DOM: Filter applied before paint

    Browser->>Theme: Execute theme.js
    Theme->>Theme: Signal window._themeReady = true
    Theme->>DOM: Check window._wallpaperReady

    Browser->>Wallpaper: Execute wallpaper.js
    Wallpaper->>Wallpaper: Preload background image
    Wallpaper->>Wallpaper: Signal window._wallpaperReady = true
    Wallpaper->>Theme: Call hideLoadingScreen()
    
    Theme->>DOM: Add fade-out class to `#LoadingScreen`
    DOM->>DOM: Opacity transition 0.3s
    Theme->>DOM: Hide loading screen on transitionend
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested labels

ui/ux, refactor

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description includes substantial technical detail about the changes but lacks key template sections: no Related Issues links, no Visual Changes/Screenshots, and all checklist items are unchecked, indicating incomplete follow-through on the template. Fill in Related Issues section, add visual evidence (screenshots/videos) if applicable, verify all checklist items, and confirm CHANGELOG.md updates are documented.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding smooth fade-out for the loading screen and preloading the wallpaper, which are the primary objectives of this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@itz-rj-here itz-rj-here added work-in-progress Under active development or being worked on by the assignee. Not ready to close or merge yet bugfix Fixes existing bugs or regressions labels Apr 5, 2026
@itz-rj-here itz-rj-here linked an issue Apr 5, 2026 that may be closed by this pull request
2 tasks
@itz-rj-here

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 5, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@itz-rj-here itz-rj-here requested a review from prem-k-r April 5, 2026 12:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/preload.js`:
- Line 10: The current line always overwrites the CSS default with black when
localStorage lacks LoadingScreenColor; change the behavior so you only call
document.documentElement.style.setProperty for the '--Loading-Screen-Color' when
localStorage.getItem('LoadingScreenColor') returns a non-null/non-empty value
(i.e., keep the built-in theme CSS default when no stored value exists) —
inspect and update the code using the existing
localStorage.getItem('LoadingScreenColor') check and the
document.documentElement.style.setProperty call to be conditional rather than
using the "|| '#000000ff'" fallback.
- Around line 17-23: Preload currently computes isDarkMode only from
preferredTheme (and system preference), which misses legacy users whose legacy
key enableDarkModeCheckboxState === "checked" was migrated later; update
preload.js to also read localStorage.getItem('enableDarkModeCheckboxState') and
treat that value === "checked" as equivalent to preferredTheme === "dark" when
computing isDarkMode (i.e., set isDarkMode = true if legacy flag is checked OR
existing preferredTheme logic is true), and keep existing
selectedTheme/bgType/isColorBg handling unchanged so legacy users get the dark
first paint.

In `@scripts/wallpaper.js`:
- Around line 174-179: The blob URL created by URL.createObjectURL (imageUrl) is
only revoked on the saved-random branch, causing leaks on the upload/restore and
stale-random paths; update the logic so every created imageUrl is revoked: call
URL.revokeObjectURL(imageUrl) after preloadAndApplyBackground(imageUrl) resolves
in the upload/restore branch (e.g., inside the .then of
preloadAndApplyBackground before or after signalWallpaperReady()), and ensure
the stale-random/saved-random flow also revokes any unused or temporary imageUrl
immediately after use (or immediately if it isn’t passed to
preloadAndApplyBackground). Target the usage sites around imageUrl,
URL.createObjectURL, preloadAndApplyBackground, and signalWallpaperReady to add
the revoke calls so no branch leaves the blob URL alive.
- Around line 141-156: The daily/midnight refresh path currently calls
signalWallpaperReady() immediately after fetch/save instead of waiting for the
image to decode; update the refresh logic to call
preloadAndApplyBackground(imageUrl) (the helper that decodes and applies the
image) and only call signalWallpaperReady() after that Promise resolves. Replace
the direct apply/notify calls in the midnight refresh branch and the similar
code around the other occurrence (previously at the block near the code that
runs at lines corresponding to the second occurrence) so both use
preloadAndApplyBackground(imageUrl). Ensure toggleBackgroundType(true) and
setting of --bg-image remain inside preloadAndApplyBackground and that the
refresh code awaits it before signaling readiness.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55117c5e-75be-44b9-b459-8ce23ff2f02c

📥 Commits

Reviewing files that changed from the base of the PR and between dc4519c and aa8668e.

📒 Files selected for processing (4)
  • scripts/preload.js
  • scripts/theme.js
  • scripts/wallpaper.js
  • style.css

Comment thread scripts/preload.js

// Set Loading Screen Color before Everything Loads
document.documentElement.style.setProperty('--Loading-Screen-Color', localStorage.getItem('LoadingScreenColor') || "#bbd6fd");
document.documentElement.style.setProperty('--Loading-Screen-Color', localStorage.getItem('LoadingScreenColor') || "#000000ff");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The fallback color is now wrong for the default-theme path.

LoadingScreenColor is only written from the explicit theme-apply paths in scripts/theme.js. Users who stay on the built-in blue theme never hit those writes, so this falls back to black on every load instead of the real default background.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/preload.js` at line 10, The current line always overwrites the CSS
default with black when localStorage lacks LoadingScreenColor; change the
behavior so you only call document.documentElement.style.setProperty for the
'--Loading-Screen-Color' when localStorage.getItem('LoadingScreenColor') returns
a non-null/non-empty value (i.e., keep the built-in theme CSS default when no
stored value exists) — inspect and update the code using the existing
localStorage.getItem('LoadingScreenColor') check and the
document.documentElement.style.setProperty call to be conditional rather than
using the "|| '#000000ff'" fallback.

Comment thread scripts/preload.js
Comment on lines +17 to +23
const preferredTheme = localStorage.getItem('preferredTheme');
const selectedTheme = localStorage.getItem('selectedTheme');
const savedBgType = localStorage.getItem('bgType');
const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

const isDarkMode = preferredTheme === 'dark' || (preferredTheme === 'system' && systemDark);
const isColorBg = savedBgType !== 'wallpaper'; // default to color if not saved

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Legacy dark-mode users still miss the preload filter.

theme.js still migrates enableDarkModeCheckboxState === "checked" to preferredTheme = "dark" at startup. Because this preload path only reads preferredTheme, those users still get the light first paint on their first post-upgrade load.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/preload.js` around lines 17 - 23, Preload currently computes
isDarkMode only from preferredTheme (and system preference), which misses legacy
users whose legacy key enableDarkModeCheckboxState === "checked" was migrated
later; update preload.js to also read
localStorage.getItem('enableDarkModeCheckboxState') and treat that value ===
"checked" as equivalent to preferredTheme === "dark" when computing isDarkMode
(i.e., set isDarkMode = true if legacy flag is checked OR existing
preferredTheme logic is true), and keep existing selectedTheme/bgType/isColorBg
handling unchanged so legacy users get the dark first paint.

Comment thread scripts/wallpaper.js
Comment on lines +141 to +156
// Helper to preload and decode an image before applying it as background
function preloadAndApplyBackground(imageUrl) {
return new Promise((resolve) => {
const img = new Image();
img.src = imageUrl;
img.decode().then(() => {
document.body.style.setProperty("--bg-image", `url(${imageUrl})`);
toggleBackgroundType(true);
resolve();
}).catch(() => {
// Fallback: apply even if decode fails
document.body.style.setProperty("--bg-image", `url(${imageUrl})`);
toggleBackgroundType(true);
resolve();
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The daily refresh path still hides before the new wallpaper is decode-ready.

This branch bypasses preloadAndApplyBackground(), so signalWallpaperReady() runs once fetch/save finish, not once the new image has decoded. That leaves the midnight refresh path still vulnerable to the wallpaper pop-in this PR is trying to eliminate.

Also applies to: 183-187

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/wallpaper.js` around lines 141 - 156, The daily/midnight refresh path
currently calls signalWallpaperReady() immediately after fetch/save instead of
waiting for the image to decode; update the refresh logic to call
preloadAndApplyBackground(imageUrl) (the helper that decodes and applies the
image) and only call signalWallpaperReady() after that Promise resolves. Replace
the direct apply/notify calls in the midnight refresh branch and the similar
code around the other occurrence (previously at the block near the code that
runs at lines corresponding to the second occurrence) so both use
preloadAndApplyBackground(imageUrl). Ensure toggleBackgroundType(true) and
setting of --bg-image remain inside preloadAndApplyBackground and that the
refresh code awaits it before signaling readiness.

Comment thread scripts/wallpaper.js
Comment on lines 174 to +179
const imageUrl = URL.createObjectURL(blob);

if (imageType === "upload") {
document.body.style.setProperty("--bg-image", `url(${imageUrl})`);
toggleBackgroundType(true);
preloadAndApplyBackground(imageUrl).then(() => {
signalWallpaperReady();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

imageUrl now leaks on two branches.

The Blob URL is created before the branch, but only the saved-random path revokes it. The upload restore path keeps it alive for the life of the tab, and the stale-random refresh path leaks an unused URL immediately.

Also applies to: 183-194

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/wallpaper.js` around lines 174 - 179, The blob URL created by
URL.createObjectURL (imageUrl) is only revoked on the saved-random branch,
causing leaks on the upload/restore and stale-random paths; update the logic so
every created imageUrl is revoked: call URL.revokeObjectURL(imageUrl) after
preloadAndApplyBackground(imageUrl) resolves in the upload/restore branch (e.g.,
inside the .then of preloadAndApplyBackground before or after
signalWallpaperReady()), and ensure the stale-random/saved-random flow also
revokes any unused or temporary imageUrl immediately after use (or immediately
if it isn’t passed to preloadAndApplyBackground). Target the usage sites around
imageUrl, URL.createObjectURL, preloadAndApplyBackground, and
signalWallpaperReady to add the revoke calls so no branch leaves the blob URL
alive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Fixes existing bugs or regressions work-in-progress Under active development or being worked on by the assignee. Not ready to close or merge yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Background flashes

1 participant