I'm trying to hot switch the theme stylesheet with my service worker.
I created a theme for Jekyll (to use with GitHub Pages), and wanted the ability to replace between dark theme and light theme ("turn off the lights") and make that consistent.
Right now, this is achieved by saving the state to localstorage
and replacing the theme link
element according to that.
the problem is, that the check happens after the original white theme is fetched so the user sees a flash of the white theme even if localstorage
have the dark theme set
here's how it behaves at the moment (the buttons are on the top right): https://thatkookooguy.github.io/postmortem-demo/postmortem/002
after changing the theme, try navigating to another page. the page loads with the white theme, and after that changes to the dark theme
I thought I can solve this problem by switching the script inside a service worker, since I can addEventListener
to the fetch
event and replace it in the promise chain with another fetch.
But the code doesn't seem to work. If I add a debug point inside the event callback, it's never triggered (not on assets and not on actual page fetches)
HTML Head
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet prefetch' href='//fonts.googleapis.com/css?family=Fredoka+One|Righteous'>
<!-- THIS SHOULD BE INTERCEPTED BY THE SERVICE WORKER -->
<link id="bulma-theme" rel="stylesheet prefetch" href="//unpkg.com/bulmaswatch/default/bulmaswatch.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="{{ site.baseurl }}/assets/style.css" />
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('{{ site.baseurl }}/assets/sw.js')
.then(function() {
console.log("Service Worker Registered");
});
}
</script>
</head>
Service Worker Code
importScripts('localforage.min.js');
// remember the theme
this.onmessage = function(msg) {
if (!msg.data.theme) {
return;
}
localforage.setItem('theme', msg.data.theme);
}
// replace link fetching with correct theme
this.addEventListener('fetch', function(event) {
console.log(event.request.url);
if (event.request.url.contains('/bulmaswatch/')) {
return localforage.getItem('theme')
.then(function(theme) {
if (theme === 'kb-dark-theme') {
let darkThemeRequest = new Request(request.url.replace('bulmaswatch/default/', 'bulmaswatch/superhero/'), {
method: request.method,
headers: request.headers,
mode: 'same-origin',
credentials: request.credentials,
redirect: 'manual'
});
return fetch(darkThemeRequest);
}
return fetch(event.request);
})
}
});
// Log SW installation
this.addEventListener('install', function(event) {
console.log('[SW]: installing....');
});
// Log SW activation
this.addEventListener('activate', function activator(event) {
console.log('[SW]: activate!');
});
When debugging in chrome (and chrome canary, since I heard there's better service worker debugging support there), the callback on the fetch
even never called. no request was ever logged.
I do see the service worker registered and active in chrome's dev tools. and I see the other events logging in the console.
Also, when debugging and putting a breakpoint at the start of the script, it looks like the bulmaswatch.min.css
was already fetched.
any idea what I'm missing? :-)
thanks