MInimize Visual Distraction of Re-Rendering to Dark Mode on Page Load

I have a Liveview application that offers a Dark mode toggle. The setting is stored in browser localStorage and Dark mode is rendered via Hooks after the liveSocket is connected.

With the static HTML rendering in normal mode (dark mode disabled), for those who want to store their mode preferences between sessions, there is a distinctive visual transition between normal mode and dark mode, as the page first renders statically in normal mode, then re-renders in Dark Mode after the liveSocket connects.

Are there any good practices that can be applied to reduce or eliminate that visual transition distraction during initial page loading?

You can see an example at https://shiptoaster.com

I’m using the following Liveview references:
https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#connected?/1

https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#get_connect_params/1

Update: The following article discusses how to use localStorage settings and insert them into the session so that the server can pass browser based settings on the first render. I will experiment with that potential solution.
https://alex-min.fr/live-view-browser-timezone/

Personally, it sounds to me like you need the server to know about it on first load, such as with a cookie.

3 Likes

I gave up on static rendering. I only have a loading... rendered statically so the visual glitch from static to live is there but tolerable.

1 Like

I was able to solve my dark mode rendering problem without needing to notify the server via a cookie, by changing app.js to examine the dark mode setting stored in localStorage and to change the DOM, as required, during initial load of app.js. Previously, I had that code in a Hooks mounted event in app.js

1 Like

Hi! Would you be able to share your code by any chance?
I’ve been struggling with this for too long…

In app.js, at top:

...
import {LiveSocket} from "phoenix_live_view"

let dark = localStorage.getItem("dark")
if (dark == null) {
	dark = "false"
}

if (dark == "true") {
	dark_mode(true)
} else {
	dark_mode(false)
}

let Hooks = {}

Hooks.Dark = {
	mounted() {
		console.log("Dark hook id: " + this.el.id)
	    if (this.el.id == "dark") {
	    	this.el.addEventListener("input", e => {
	    		console.log("setting dark listener")
	    		localStorage.setItem("dark", e.target.checked)
	    	})
	    }
	    let dark = localStorage.getItem("dark")
	    if (dark == null) {
	    	dark = "false"
	    }
	    if (dark == true) {
	    	dark = "true"
	    }
	    this.pushEvent("restore", {
	      dark: dark,
	    })
	},

	updated() {
		var dark_toggle = document.getElementById("dark")
	    console.log("dark_mode checked value: " + dark_toggle.checked)
		if (dark_toggle.checked) {
			dark_mode(true)
		} else {
			dark_mode(false)
		}
	}
}

In app.js at bottom:

function dark_mode(is_dark) {
   var body = document.body
   if (is_dark == true) {
   	body.classList.add("dark-mode")
   } else {
	body.classList.remove("dark-mode");
   }
}
1 Like