diff --git a/scripts/preload.js b/scripts/preload.js index d7f1030c..aad047b5 100644 --- a/scripts/preload.js +++ b/scripts/preload.js @@ -7,4 +7,23 @@ */ // 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"); + +// Early dark mode detection to prevent light→dark flash +// The CSS dark mode filter (invert + hue-rotate) normally depends on DOM elements +// that don't exist yet. Pre-apply the filter on so the loading screen +// matches the dark mode appearance from the first paint. +(function () { + 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 + const isBlackTheme = selectedTheme === 'dark'; + + if (isDarkMode && isColorBg && !isBlackTheme) { + document.documentElement.classList.add('early-dark-filter'); + } +})(); diff --git a/scripts/theme.js b/scripts/theme.js index 365ab938..c214d41f 100644 --- a/scripts/theme.js +++ b/scripts/theme.js @@ -84,6 +84,20 @@ syncThemeChange(systemTheme); initializeThemeMode(); })(); +// Hide loading screen with smooth fade-out +function hideLoadingScreen() { + const loadingScreen = document.getElementById("LoadingScreen"); + if (!loadingScreen || loadingScreen.classList.contains("fade-out")) return; + loadingScreen.classList.add("fade-out"); + loadingScreen.addEventListener("transitionend", () => { + loadingScreen.style.display = "none"; + }, { once: true }); + // Fallback in case transitionend doesn't fire + setTimeout(() => { + loadingScreen.style.display = "none"; + }, 500); +} + document.addEventListener("DOMContentLoaded", () => { // Check for custom color const storedCustomColor = localStorage.getItem(customThemeStorageKey); @@ -106,8 +120,20 @@ document.addEventListener("DOMContentLoaded", () => { } } - // Remove Loading Screen when the DOM and the theme has loaded - document.getElementById("LoadingScreen").style.display = "none"; + // Signal that theme is ready; loading screen will be hidden + // once both theme and wallpaper are ready + window._themeReady = true; + if (window._wallpaperReady) { + hideLoadingScreen(); + } + + // Safety: ensure loading screen is hidden even if wallpaper never signals + setTimeout(() => { + if (!window._wallpaperReady) { + window._wallpaperReady = true; + hideLoadingScreen(); + } + }, 3000); // Stop blinking of some elements when the page is reloaded setTimeout(() => { diff --git a/scripts/wallpaper.js b/scripts/wallpaper.js index 54e5afd4..e07c779f 100644 --- a/scripts/wallpaper.js +++ b/scripts/wallpaper.js @@ -124,7 +124,36 @@ async function applyRandomImage(showConfirmation = true) { // Function to update the background type attribute function toggleBackgroundType(hasWallpaper) { - document.body.setAttribute("data-bg", hasWallpaper ? "wallpaper" : "color"); + const bgType = hasWallpaper ? "wallpaper" : "color"; + document.body.setAttribute("data-bg", bgType); + localStorage.setItem("bgType", bgType); + document.documentElement.classList.remove("early-dark-filter"); +} + +// Signal that wallpaper is ready and trigger loading screen hide if theme is also ready +function signalWallpaperReady() { + window._wallpaperReady = true; + if (window._themeReady) { + hideLoadingScreen(); + } +} + +// 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(); + }); + }); } // Check and update image on page load @@ -137,6 +166,7 @@ function checkAndUpdateImage() { // No image or invalid data if (!blob || !savedTimestamp || isNaN(lastUpdate)) { toggleBackgroundType(false); + signalWallpaperReady(); return; } @@ -144,26 +174,30 @@ function checkAndUpdateImage() { const imageUrl = URL.createObjectURL(blob); if (imageType === "upload") { - document.body.style.setProperty("--bg-image", `url(${imageUrl})`); - toggleBackgroundType(true); + preloadAndApplyBackground(imageUrl).then(() => { + signalWallpaperReady(); + }); return; } if (lastUpdate.toDateString() !== now.toDateString()) { // Refresh random image if a new day - applyRandomImage(false); + applyRandomImage(false).then(() => { + signalWallpaperReady(); + }); } else { // Reapply the saved random image - document.body.style.setProperty("--bg-image", `url(${imageUrl})`); - toggleBackgroundType(true); + preloadAndApplyBackground(imageUrl).then(() => { + signalWallpaperReady(); + // Clean up the Blob URL after setting the background + setTimeout(() => URL.revokeObjectURL(imageUrl), 1500); + }); } - - // Clean up the Blob URL after setting the background - setTimeout(() => URL.revokeObjectURL(imageUrl), 1500); }) .catch((error) => { console.error("Error loading image details:", error); toggleBackgroundType(false); + signalWallpaperReady(); }); } diff --git a/style.css b/style.css index 07a94cda..a7a779f5 100644 --- a/style.css +++ b/style.css @@ -221,6 +221,20 @@ body { margin: 0; } +/* Early dark mode filter applied by preload.js before DOM elements exist. + Prevents light→dark flash by pre-applying the invert filter on . + Removed by wallpaper.js once the proper CSS selectors can take over. */ +html.early-dark-filter { + -webkit-filter: invert(1) hue-rotate(180deg); + filter: invert(1) hue-rotate(180deg); + + & #darkTheme, + & .favicon { + -webkit-filter: invert(1) hue-rotate(180deg); + filter: invert(1) hue-rotate(180deg); + } +} + /* Apply dark mode when: Dark Mode is selected manually, OR System Theme is selected */ html:has(body[data-bg="color"] .menuBar .themeSegment[data-active="dark"]):not( :has(#darkTheme:checked) @@ -230,13 +244,13 @@ html:has( .menuBar .themeSegment[data-active="system"] ):not(:has(#darkTheme:checked)) { - filter: invert(1) hue-rotate(180deg); -webkit-filter: invert(1) hue-rotate(180deg); + filter: invert(1) hue-rotate(180deg); & #darkTheme, & .favicon { - filter: invert(1) hue-rotate(180deg); -webkit-filter: invert(1) hue-rotate(180deg); + filter: invert(1) hue-rotate(180deg); } & .menuBar, @@ -268,13 +282,13 @@ body[data-bg="wallpaper"][sysTheme="systemDark"]:has( .menuBar .themeSegment[data-active="system"] ):not(:has(#darkTheme:checked)) :is(.menuCont, .bookmark-sidebar) { - filter: invert(1) hue-rotate(180deg); -webkit-filter: invert(1) hue-rotate(180deg); + filter: invert(1) hue-rotate(180deg); & #darkTheme, & .favicon { - filter: invert(1) hue-rotate(180deg); -webkit-filter: invert(1) hue-rotate(180deg); + filter: invert(1) hue-rotate(180deg); } --whitishColor-blue: #f2f2f2; @@ -4330,6 +4344,13 @@ body[data-bg="wallpaper"] .dropdown-content { width: 100%; inset: 0; z-index: 99999; + opacity: 1; + transition: opacity 0.3s ease; +} + +#LoadingScreen.fade-out { + opacity: 0; + pointer-events: none; } .toggleTextsCont .ttcont:has(#hideWeatherBox) {