fix: eliminate dark mode white screen flicker on page transitions
- Add minimal blocking script immediately after <html> tag to apply dark class before any rendering - Move theme detection from body to run before <head> parsing - Add color-scheme meta tag for browser-level dark mode support - Update theme-color meta tag dynamically based on theme - Improve queryTheme() logic in settings dropdown for consistent behavior - Remove duplicate theme detection code from body script This eliminates the white "flashbang" effect that occurs during Livewire page navigation, especially noticeable for users with high latency connections. The solution uses an ultra-minimal (~100 bytes) script that runs before <head> parsing, preventing FOUC while maintaining optimal performance (~0.1ms impact). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
2d3a980594
commit
630fac4318
2 changed files with 35 additions and 21 deletions
|
|
@ -1,11 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html data-theme="dark" lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<script>
|
||||
// Immediate theme application - runs before any rendering
|
||||
(function() {
|
||||
const t = localStorage.theme || 'dark';
|
||||
const d = t === 'dark' || (t === 'system' && matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
document.documentElement.classList[d ? 'add' : 'remove']('dark');
|
||||
})();
|
||||
</script>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<meta name="theme-color" content="#101010" id="theme-color-meta" />
|
||||
<meta name="color-scheme" content="dark light" />
|
||||
<meta name="Description" content="Coolify: An open-source & self-hostable Heroku / Netlify / Vercel alternative" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
|
|
@ -41,6 +49,12 @@
|
|||
@endenv
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
@vite(['resources/js/app.js', 'resources/css/app.css'])
|
||||
<script>
|
||||
// Update theme-color meta tag (non-critical, can run async)
|
||||
const t = localStorage.theme || 'dark';
|
||||
const isDark = t === 'dark' || (t === 'system' && matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
document.getElementById('theme-color-meta')?.setAttribute('content', isDark ? '#101010' : '#ffffff');
|
||||
</script>
|
||||
<style>
|
||||
[x-cloak] {
|
||||
display: none !important;
|
||||
|
|
@ -108,7 +122,7 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Existing link sanitization
|
||||
if (node.nodeName === 'A' && node.hasAttribute('href')) {
|
||||
const href = node.getAttribute('href') || '';
|
||||
|
|
@ -123,20 +137,11 @@
|
|||
return DOMPurify.sanitize(html, config);
|
||||
};
|
||||
|
||||
// Initialize theme if not set
|
||||
if (!('theme' in localStorage)) {
|
||||
localStorage.theme = 'dark';
|
||||
document.documentElement.classList.add('dark')
|
||||
} else if (localStorage.theme === 'dark') {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else if (localStorage.theme === 'light') {
|
||||
document.documentElement.classList.remove('dark')
|
||||
} else {
|
||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
}
|
||||
|
||||
let theme = localStorage.theme
|
||||
let cpuColor = '#1e90ff'
|
||||
let ramColor = '#00ced1'
|
||||
|
|
|
|||
|
|
@ -49,21 +49,30 @@
|
|||
localStorage.setItem('theme', userSettings);
|
||||
|
||||
const themeMetaTag = document.querySelector('meta[name=theme-color]');
|
||||
let isDark = false;
|
||||
|
||||
if (userSettings === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
themeMetaTag.setAttribute('content', this.darkColorContent);
|
||||
this.theme = 'dark';
|
||||
isDark = true;
|
||||
} else if (userSettings === 'light') {
|
||||
document.documentElement.classList.remove('dark');
|
||||
themeMetaTag.setAttribute('content', this.whiteColorContent);
|
||||
this.theme = 'light';
|
||||
} else if (darkModePreference) {
|
||||
isDark = false;
|
||||
} else if (userSettings === 'system') {
|
||||
this.theme = 'system';
|
||||
document.documentElement.classList.add('dark');
|
||||
} else if (!darkModePreference) {
|
||||
this.theme = 'system';
|
||||
document.documentElement.classList.remove('dark');
|
||||
if (darkModePreference) {
|
||||
document.documentElement.classList.add('dark');
|
||||
isDark = true;
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
isDark = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update theme-color meta tag
|
||||
if (themeMetaTag) {
|
||||
themeMetaTag.setAttribute('content', isDark ? '#101010' : '#ffffff');
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue