Themes with MultiGlancer
Published May 2024
I’ve been using MultiGlancer to help me with my UI layout, which is such a fantastic tool. It allows you to run your web app locally and see how it is rendered on different screen sizes and I find it invaluable when doing any style work. But I always felt one thing was missing - viewing different themes side-by-side.
Being a software developer, and having access to the codebase from GitHib I decided to extend it myself. I had intended raising a PR for it, but it requires a change to the web app being viewed to work so instead of a PR I’m documenting how I achieved it here.
First, MultiGlancer has a View
object defined in views.ts
which i extended with a new theme: string
property:
export interface View {
name: string
width: number
height: number
enabled: boolean
scale: number
zoom: number
theme: string
}
Second, I needed to add a theme to each of the items in appState.ts
and I doubled them up for “light” and “dark” themese:
export const appState = ref({
zoom: 0.3,
url: 'http://localhost:5173',
views: [
{ scale: 1, enabled: true, name: 'Pixel 5', width: 393, height: 851, theme: 'light' },
{ scale: 1, enabled: true, name: 'Pixel 5 - dark', width: 393, height: 851, theme: 'dark' },
{ scale: 1, enabled: true, name: 'iPhone 12 Pro', width: 390, height: 844, theme: 'light' },
{ scale: 1, enabled: true, name: 'iPhone 12 Pro - dark', width: 390, height: 844, theme: 'dark' },
{ scale: 1, enabled: true, name: 'SXGA', width: 1280, height: 1024, theme: 'light' },
{ scale: 1, enabled: true, name: 'SXGA - dark', width: 1280, height: 1024, theme: 'dark' },
{ scale: 1, enabled: true, name: 'HD', width: 1366, height: 768, theme: 'light' },
{ scale: 1, enabled: true, name: 'HD - dark', width: 1366, height: 768, theme: 'dark' },
{ scale: 1, enabled: true, name: 'HD+', width: 1600, height: 900, theme: 'light' },
{ scale: 1, enabled: true, name: 'HD+ - dark', width: 1600, height: 900, theme: 'dark' },
{ scale: 1, enabled: true, name: 'FHD', width: 1920, height: 1080, theme: 'light' },
{ scale: 1, enabled: true, name: 'FHD - dark', width: 1920, height: 1080, theme: 'dark' },
{ scale: 1, enabled: true, name: 'WUXGA', width: 1920, height: 1200, theme: 'light' },
{ scale: 1, enabled: true, name: 'WUXGA - dark', width: 1920, height: 1200, theme: 'dark' },
{ scale: 1, enabled: true, name: 'QHD', width: 2560, height: 1440, theme: 'light' },
{ scale: 1, enabled: true, name: 'QHD - dark', width: 2560, height: 1440, theme: 'dark' },
{ scale: 1, enabled: true, name: 'WQHD', width: 3440, height: 1440, theme: 'light' },
{ scale: 1, enabled: true, name: 'WQHD - dark', width: 3440, height: 1440, theme: 'dark' },
{ scale: 1, enabled: true, name: 'UHD', width: 3840, height: 2160, theme: 'light' },
{ scale: 1, enabled: true, name: 'UHD - dark', width: 3840, height: 2160, theme: 'dark' },
]
})
In ViewsComponent.vue
I added code to see the theme, and to post a message to the iframe for each view:
...
<div style="padding: 0 1em; font-weight: 600;">
{{ view.name }}
{{ view.width }}
x
{{view.height }}
{{ view.theme }}
</div>
...
<foreignObject :width="view.width" :height="view.height">
<iframe ref="iframes" style="border: none;" :src="appState.url" :width="view.width" :height="view.height" />
</foreignObject>
...
import { ref, onMounted } from 'vue';
import { appState } from 'src/stores/appState';
let iframes = ref([]);
onMounted(() => {
console.log(appState.value.views)
for (let i = 0; i < appState.value.views.length; i++) {
let view = appState.value.views[i];
if (view.enabled) {
let iframe = iframes.value[i];
iframe.onload = function () {
iframe.contentWindow.postMessage({ theme: view.theme }, '*');
};
}
}
});
...
And finally I edited my own app to recieve the message and apply the theme. As I’m using a Svelte project, I added this to app.html
:
<script>
window.addEventListener("message", function (event) {
if (event.origin !== "http://localhost:9000") {
return
}
if (event.data && event.data.theme) {
if (event.data.theme != "") {
document.documentElement.setAttribute("data-theme", event.data.theme)
}
}
})
</script>
And the result should be something like this: