Issue with AUI Design Tokens and Dark Mode Theme in iFrame

Description:

I am experiencing an issue with the implementation of AUI Design Tokens for the Dark Mode theme. Despite following the methods described in the AUI Dark Mode documentation, the theme is only working correctly on the Firefox browser. The plugin is rendered inside an iframe and is loading the AJS module statically.

Technical Details:

  • AUI Version: 9.11
  • Browsers Tested: Firefox (works), Chrome (issue), Safari (issue), MS Edge (issue)
  • Environment: Confluence 9.0.0-m123, MacOS / Linux
  • atlassian-plugin.xml:
<web-resource key="balsamiqWireframesEditor" >
		<param name="batch" value="false"/>
		<resource type="download" name="b4/version.jsonp" location="editor-b4/version.jsonp"/>
		<resource type="download" name="b4/balsamiqmockupslogo.999.0.999.js" location="editor-b4/balsamiqmockupslogo.999.0.999.js"/>
		<resource type="download" name="b4/balsamiqsplashscreen.999.0.999.js" location="editor-b4/balsamiqsplashscreen.999.0.999.js"/>
		<resource type="download" name="b4/loading-screen.999.0.999.js" location="editor-b4/loading-screen.999.0.999.js"/>
		<resource type="download" name="b4/editor-web.999.0.999.js" location="editor-b4/editor-web.999.0.999.js"/>
		<resource type="download" name="all.css" location="css/all.css"/>
		<resource type="download" name="connect-api.js" location="lib/connect-api.js"/>
		<resource type="download" name="all_bundle.js" location="js/all_bundle.js"/>

		<!-- AUI DEPENDENCIES: BEGIN -->
		<resource type="download" name="aui-prototyping-9.11.0.min.css" location="css/aui-prototyping-9.11.0.min.css"/>
		<resource type="download" name="fonts/atlassian-icons.woff" location="fonts/atlassian-icons.woff"/>
		<resource type="download" name="fonts/atlassian-icons.ttf" location="fonts/atlassian-icons.ttf"/>
		<resource type="download" name="fonts/atlassian-icons.eot" location="fonts/atlassian-icons.eot"/>
		<resource type="download" name="fonts/adgs-icons.woff" location="fonts/adgs-icons.woff"/>
		<resource type="download" name="fonts/adgs-icons.ttf" location="fonts/adgs-icons.ttf"/>
		<resource type="download" name="fonts/adgs-icons.eot" location="fonts/adgs-icons.eot"/>
		<resource type="download" name="aui-prototyping-9.11.0.min.js" location="lib/aui-prototyping-9.11.0.min.js"/>
		<resource type="download" name="aui-prototyping-design-tokens-api-full-9.11.0.min.js" location="lib/aui-prototyping-design-tokens-api-full-9.11.0.min.js"/>
		<resource type="download" name="themes/light.min.js" location="themes/light.min.js"/>
		<resource type="download" name="themes/dark.min.js" location="themes/dark.min.js"/>
		<resource type="download" name="themes/spacing.min.js" location="themes/spacing.min.js"/>
		<resource type="download" name="themes/shape.min.js" location="themes/shape.min.js"/>
		<resource type="download" name="themes/typography-minor3.min.js" location="themes/typography-minor3.min.js"/>
		<resource type="download" name="themes/typography-adg3.min.js" location="themes/typography-adg3.min.js"/>
		<resource type="download" name="themes/light-new-input-border.min.js" location="themes/light-new-input-border.min.js"/>
		<resource type="download" name="themes/light-future.min.js" location="themes/light-future.min.js"/>
		<resource type="download" name="themes/legacy-light.min.js" location="themes/legacy-light.min.js"/>
		<resource type="download" name="themes/legacy-dark.min.js" location="themes/legacy-dark.min.js"/>
		<resource type="download" name="themes/dark-new-input-border.min.js" location="themes/dark-new-input-border.min.js"/>
		<resource type="download" name="themes/dark-future.min.js" location="themes/dark-future.min.js"/>
		<resource type="download" name="jquery-3.4.1.min.js" location="lib/jquery-3.4.1.min.js"/>
		<!-- AUI DEPENDENCIES: END -->	
	</web-resource>
  • BalsamiqMockupsPreview.vm:
...
<head>
    <title>Balsamiq Wireframes</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>

    <link rel="stylesheet" href="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/aui-prototyping-9.11.0.min.css" media="all">
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/jquery-3.4.1.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/aui-prototyping-9.11.0.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/aui-prototyping-design-tokens-api-full-9.11.0.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/light.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/dark.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/spacing.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/shape.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/typography-minor3.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/typography-adg3.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/light-new-input-border.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/light-future.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/legacy-light.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/legacy-dark.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/dark-new-input-border.min.js"></script>
    <script src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/themes/dark-future.min.js"></script>

    <link rel="stylesheet" href="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/all.css" media="all">

    <script type="text/javascript" src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/connect-api.js"></script>

    <script type="text/javascript" src='$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/js/sql-0.4.0-memory-growth.js'></script>
    <script type="text/javascript" src="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/all_bundle.js"></script>

    <link rel="stylesheet" href="$cp/download/resources/com.balsamiq.confluence.plugins.mockups:balsamiqWireframesEditor/fonts/opensans/opensans.css" media="all">
</head>
...
  • all_bundle.js:
(async function() {
    ...
            console.log("--- AJS --- Setting global theme");
            try {
                await AJS.DesignTokens.setGlobalTheme();
            } catch (e) {
                console.log("--- AJS --- Error setting global theme: " + e);
            }

            try {
                console.log("--- AJS --- Setting theme observer ");
                observer = new AJS.DesignTokens.ThemeMutationObserver((newTheme) => {
                    console.log(newTheme); // { light: light, dark: dark, ... }
                });

                observer.observe();

            } catch (e) {
                console.log("--- AJS --- Error setting theme observer: " + e);
            }
   ...
    }
})();

Video Demo:
https://www.youtube.com/watch?v=gYx2mOs2Cqo

Expected Behavior:

The Dark Mode theme should be consistently applied across all browsers when using AUI Design Tokens, regardless of the iframe setup.

Actual Behavior:

The Dark Mode theme is only applied correctly in Firefox. In other browsers like Chrome and Safari, the theme does not render as expected.

Request for Assistance:

I would appreciate any guidance or suggestions on how to resolve this issue. Specifically:

  • Are there known compatibility issues with AUI Design Tokens and certain browsers?
  • Could the static loading of the AJS module within an iframe be causing this issue?
  • Any additional debugging steps or solutions that could help in resolving this problem?

Thank you in advance for your assistance!

1 Like

Hi @AndreaSerra,
Thanks for reaching out about the issue you’re experiencing.
We have a guide for implementing dark theme inside iframes, using the web-resources, could you please give this a try and see if it resolves the issue?

In our Jira Connect app using AUI I have potentially the same problem.
Everything works fine in Firefox, but in Edge and potentially other browsers the behavior is “different”.
In Edge, our app in the iframe will always match the browser’s theme. It basically ignores the theme setting from the personal settings.
So e.g. if the theme in the personal settings is set to light, but the browser is set to dark, Jira will be light and our app will be dark.

2 Likes

Thanks for the quick response!

I gave the suggested guide for implementing the dark theme inside iframes a try. While doing so, I managed to solve the issue using a pure JavaScript solution with the message event handler.

Here’s the solution that worked for me:

async function configureTheme() {
    let themeConfigured = false;
    try {
        // only works if we are inside an iframe
        const element = parent && parent.document ? parent.document.documentElement : undefined;
        if (element) {
            const THEME_DATA_ATTRIBUTE = 'data-theme';
            const COLOR_MODE_ATTRIBUTE = 'data-color-mode';
            const colorMode = element.getAttribute(COLOR_MODE_ATTRIBUTE) || '';
            const theme = element.getAttribute(THEME_DATA_ATTRIBUTE) || '';

            function parseTheme(str) {
                var result = {
                    dark: 'dark',
                    light: 'light',
                    spacing: 'spacing',
                };
                const themeKinds = ['light', 'dark', 'spacing'];
                var pairs = str.split(' ');
                pairs.forEach(pair => {
                    var [key, value] = pair.split(':');
                    if (themeKinds.includes(key)) {
                        result[key] = value;
                    }
                });

                return result;
            }

            if (colorMode) {
                const themeObject = parseTheme(theme);
                console.log("--- AJS --- Setting global theme");
                console.log("--- AJS --- Theme: ", themeObject, " Color mode: ", colorMode);
                await AJS.DesignTokens.setGlobalTheme({
                    colorMode: colorMode.toLowerCase(),
                    dark: themeObject.dark,
                    light: themeObject.light,
                    spacing: themeObject.spacing
                });
                themeConfigured = true;
            }
        }
    } catch (e) {
        console.log("--- AJS --- Error setting global theme at startup: " + e);
    } finally {
        if ( themeConfigured) {
            // add a listener to the message event to switch the theme
            window.addEventListener("message", (event) => {
                try {
                    const {type, colorMode, darkThemeKey: dark, lightThemeKey: light} = JSON.parse(event.data);
                    //log the event
                    console.log("--- AJS --- Received event: ", type);
                    if (type !== "theme.change") return;
                    //log the event data
                    console.log("--- AJS --- Event data: ", colorMode, dark, light);
                    // Activate the new theme
                    AJS.DesignTokens.setGlobalTheme({colorMode: colorMode.toLowerCase(), dark, light});
                } catch (e) {
                    console.log("--- AJS --- Error setting global theme on switch: " + e);
                }
            });
        }
    }

    return themeConfigured;
}
...
(async function() {
     await configureTheme();
     ....
    }
})();

This ensures the dark mode theme is applied correctly across all browsers when using AUI Design Tokens within an iframe.

I hope this solution can help anyone with the same issue.

Thanks again for your help!

That’s the right solution, well done @AndreaSerra ! It’s pretty much what the suggested Confluence code does too but no problem if you implement it directly.

What I would suggest besides the event listener is to avoid loading aui-prototyping and ADGS fonts directly. The AUI prototyping bundle isn’t meant for production use, it’s far better to use AUI provided by Confluence via the web-resource dependencies as needed:

# Specific AUI components, for example:
com.atlassian.auiplugin:aui-page-typography

# Theming and design token support
com.atlassian.auiplugin:aui-design-tokens-themes
com.atlassian.auiplugin:design-tokens-api

It will assure consistency with the AUI version in the product and will only load required parts of AUI, unlike the prototyping bundle.

@t-bundt This is a solution for DC and while you can load AUI and its theming resources similarly in any other app (like Atlassian Connect), the event listener won’t work in our Cloud products.