svecosystem / mode-watcher

Simple light/dark mode management for SvelteKit apps. 🌑 ←→ ☀️
https://mode-watcher.svecosystem.com
MIT License
313 stars 12 forks source link

FOUC #39

Closed jasongitmail closed 11 months ago

jasongitmail commented 11 months ago

I notice that @ollema's PR includes some handling to mitigate FOUC.

However, I still see FOUC briefly on initial load, when system is set to dark mode.

(This is from Safari, but this can be observed in Chrome by enabling "slow 3G" in the network inspector and reloading, after clearing local storage of course.)

My hypothesis is that this because the code lives within a .svelte component, rather than app.html.

It's not a big deal, and I can imagine the current design was chosen to reduce work for a dev implementing it, which seems reasonable. But I wanted to ask @ollema if he noticed this remaining FOUC and if there is a way to mitigate it fully.

ollema commented 11 months ago

hi @jasongitmail.

as you noticed, my patch also included mitigations to avoid FOUC.

this already existed in mode-watcher and in the <LightSwitch> component from skeleton that the original implementation in mode-watcher drew a lot of inspiration from. my patch did not change how this is implemented.

both before my patch and now, a snippet that sets the correct class on the html element is added to the <head> element. this means that it should be loaded and executed fairly quickly. see: https://github.com/huntabyte/mode-watcher/blob/8c71d5a01953b7444913074e85db746aea6d1e2e/src/lib/mode-watcher.svelte#L38..L41

in fact, I am not able to replicate this FOUC with dark mode. are you experimenting with a deployed app or with a dev server? maybe that is a factor.

here I'm trying the same safari timeline tool when I have deleted localStorage variables and then do a full refresh

Screenshot 2023-11-24 at 11 31 30

I wonder why this is not working for you?

jasongitmail commented 11 months ago

@ollema Found the cause. It's CSP related.

    // svelte.config.js
    ...
    csp: {
      directives: {
        'base-uri': ['none'],
        'connect-src': ['self', 'cloudflareinsights.com'],
        // 'default-src': ['self'],
        'img-src': ['*'],
        'object-src': ['none'],
        // 'script-src': ['self', 'static.cloudflareinsights.com'],
        'style-src': ['self', 'unsafe-inline'],
      },
    },
   ...

If I enable either default-src or script-src rules, either individually or both together, the FOUC occurs. If these properties exist even with an empty array value or a ['*'] value, then FOUC still occurs. Commenting those two properties out removes the FOUC.

Seems like it may be a conflict with the inline script's nonce-based CSP added by mode-watcher, where svelte.config.js's CSP rules clobber it somehow.

ollema commented 11 months ago

interesting! so is this still an issue with mode-watcher? if so, do you know how to fix it?

or should this issue be closed?

jasongitmail commented 11 months ago

Seems to be an issue with mode-watcher. I don't know the solution yet. Would be curious if you guys have ideas

ollema commented 11 months ago

see: https://github.com/sveltejs/kit/discussions/4952 for additional context

jasongitmail commented 11 months ago

oh interesting.

I might just live with the FOUC for now, rather than disable my CSP rules, and hope the core devs address that eventually.

We can close this then.