michaelekins.co.uk

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:

multiglancer with themes image

© 2015 - 2025 Michael Ekins
All rights reserved

Themes with MultiGlancer