Challenge 009 | Summer camp 2022 - part 3

Good to see you here for the last stretch of our 🏕️ summer camp 🏕️. Even though you might have spent all your annual leave hours and got your yearly dose of sun allergy (I did get mine), summer is not over, yet. We still have one more challenge to go in our three-part series on making your canvas apps look a bit better. In part 1 of summer camp, I promised we would get back to theming. As we probably all have spent way too much time adjusting the styling of our controls, I thought it would be good to have this topic included. A full challenge on theming for canvas apps.

Challenge Objectives

🎯 Learn about the power of a theme variable

🎯 Create your own theme 🎯 Make the default controls adapt to your theme by default

🤓 Why Should We Even Bother?

The core functionality of basically any app we make is an interface for our data source. The app user can view the data, adjust it, or add data to it. The look and feel of our app don't have a direct effect on the functionality of our application. Indirectly, the UI and UX are of great importance for the success of an app.

The thing is, that if users don't use the application, the functionality we put into the application is worthless. That's why we put so much effort into all the topics we discussed during this summer camp. In my opinion, improving the UI/UX of your app is all about removing any potential point of friction. A responsive app removes the friction of an inconvenient user experience. Modern controls make the app look familiar, and familiarity is frictionless by default. Styling your app according to in detail described branding guidelines also is about familiarity.

That's why these summer camp challenges are of interest. What we will learn in this challenge will not only help you improve the UI/UX of your app. but also helps sell your app to higher management. I have experienced that higher management is really sensitive to how the app looks. Despite the fact that everyone is saying that their business is data-driven, seeing an app that looks like a client-facing app really just sells.

🎓 Learn By Example

Creator Kit

In part 1 of this summer camp, we installed the Creator Kit. We will go into those things again, so if you haven't installed the Creator Kit yet, you could go through that challenge first.

In the OnStart of the template app, you can see that a variable called AppTheme is created. This is the central variable that is used throughout the application to color the components. After the AppTheme is created, there is another variable called AppThemeJSON created. The reason this is done is that the Code components need JSON as an input, whereas the Library components can deal with a regular variable.

If you create a new screen and add a library component, you can see in the properties that there is a custom property called theme. That is where you should enter the AppTheme variable. Notice that when you enter the other variable, AppThemeJSON, it will have that red wavy line underneath it that indicates something is wrong. That's how you can quickly pick the right variable type. Now try the same for a code component.

I want you to fully understand what this means. We create a variable when the application is starting, and we give that variable as an input for the theme of that component. This means that if we adjust the values in the app OnStart, all components are adjusted. So, if the branding guidelines update, we only have to update the OnStart values to make colors adjust accordingly.

Change the themePrimary value from #0078d4 to #ff0000. As you can see, the Header and Dialog now are differently colored. Pretty sweet.

The theme palette is not only the primary color but many more to make it look good. You have seen that in the OnStart property of the app. To update this completely, you can use the Fluent Theme Designer that comes with the Creator Kit. You only must specify the primary color, text color, and background color, and the theme will be created for you. On the top right of the app, you can see the Export Theme button. It will have that whole palette in a string that you can copy-paste into your OnStart property.


Why I really like the theme designer is that it includes an Accessibility checker that gives you some insight into the legibility of different controls. I get the information that the #ff0000 value we have entered for our primary color gives some issues. As mentioned earlier, the goal of UI/UX is to remove any form of friction. So, these warnings are 👎🏻🛑☠️. We took a color that probably no company uses as their primary color. But it is helpful to keep an eye out for this. If you do get such a warning, it might be better to check the brand's secondary color to see how that works regarding accessibility.

The Fluent Theme Designer app is helpful during development to just copy-paste the values. But opening the app might not always be the best fit. It's good to know that the app is 99% similar to the online theme designer. You can use that for quickly checking how the client's brand color works.

The Creator Kit is super helpful for quickly adding those components to your canvas and adjusting the coloring. I recommend using those combined with the AppTheme variable, as it is just super quick to develop. We don't want friction, but we also don't want to spend too much time (money) on it. But there is always a good reason to bring it a little further, so let's dig a little further into another great example.

Example App From Microsoft

During Challenge 008, we learned how to have some nice responsiveness from a sample app. For theming, we can also learn a lot from them. Install the Milestones app to your team. You can do that by going to https://teams.microsoft.com, select apps, search for Milestones and add it to a team.

Once the app is installed, open the Power Apps app in Teams, go to the Build tab, and select the team where you have installed the Milestones app. If you press see all, it will open in edit mode.

The first thing that I want to show is that this app does much more than just setting the theme when the app is starting. For that reason, they've created a Loading Screen. Only the essentials are in the app OnStart. The rest of the functions have been migrated to the OnVisible of the Loading Screen. This is not a performance gain, but the user perceives the wait differently. Less painful. That's why you see these loading screens everywhere. Some apps also create a loading bar this fills with each function being completed. Depending on how long the wait is that might be a nice feature. The performance itself is an important aspect of User Experience, but that could be a separate challenge. Maybe one day.

Another nice User Experience feature of this app is the First Run dialog. The snippet below contains the second and third functions in the OnVisible of the Loading Screen. The nice thing is that the user settings are saved. This way the user can set their preference, and when they come back, those settings are still there. Again, removing friction.

ClearCollect(// Collect User Settings CDS Record, if it exists
    colUserSettings,
    Filter(
        'Project User Settings',
        'Project User Settings (Views)'.'My Project User Setting'
    )
);
//if record does not exist, or was not found, create and collect
If(
    CountRows(colUserSettings) = 0,
    Set(
        gblFirstRun,
        true
    );
    ClearCollect(
        colUserSettings,
        IfError(Patch(
            'Project User Settings',
            Defaults('Project User Settings'),
            {
                Name: User().FullName,
                'Display Splash (Power Apps)': 'Display Splash (Power Apps) (Project User Settings)'.Yes
            }
        ),Blank())
    )
)

The second thing that I find worth mentioning, is that when no settings are found, the variable gblFirstRun is set to `true`. This variable is used in the Visible property of the conDialogWelcome container on the Projects Screen. This is onboarding the user to the app, but just once. Again a nice friction remover.

These two features have nothing to do with theming, but are great examples of friction erasers. I hope I convinced you that we have to search for friction and remove them, rather than just making it look nice. However, I mentioned this challenge was mostly about theming, so let's get back to the world of coloring and styling.

Go back to the OnVisible of the Loading Screen. Scroll a little until you see the PLEASE READ section. The snippet below is a part of the first function after that part. As you can see, gblThemeDark and gblThemeHiCo are set. For this, it uses the theme parameter. High Contrast is another accessibility feature that allows those with a vision disability to use your app too.

Concurrent(
    Set(
        gblThemeDark,
        Param("theme") = "dark"  Or (tglAdmin_DarkMode.Value)// Enable for Studio
    ),
    Set(
        gblThemeHiCo,
        Param("theme") = "contrast"  Or (tglAdmin_ContrastMode.Value)// Enable for Studio
    )
);

When you see the Param() function, you know you are dealing with what is called a launch parameter. You can look into the Microsoft documentation if you want to know exactly how they work, but basically, you can use these to get information from outside your app inside your app. I am still not 100% sure where the Dataverse for Teams app gets the launch parameter from, but my best guess is that it has to do with this Teams layer that you can see in the tree view.

The thing I do know for sure is that it gets the theme setting from your Teams client app into the Power App. This means that If you adjust the settings within Teams, we can use that information to make our app also either Dark or High Contrast. Isn't that super cool?

After that, the gblAppColors variable is created. This is quite similar to what the Creator Kit Template app does OnStart. It only creates a color value from the HEX text field, but this is the exact same approach. The thing where this app is significantly different compared to the Creator Kit Template app, is how it deals with this theming variable. As we have seen in the Creator Kit section, we give the variable as an input to the component. The component itself has a predefined in how these colors are used. To put it differently, We can only adjust the color, but if we want the border thicker, or not the primary color, but a neutral for example, we can't. The Dataverse for Teams app does give you this flexibility.

After the gblAppColors variable is created, another variable is created called gblAppStyles. If you look into the function that creates this new variable, you can see that it creates a variable that contains the styling specifications for many different properties of many different controls. You can also see that the value given depends on the earlier created gblThemeDark and gblThemeHiCo variables.

If you select some controls throughout the app, you can see that this gblAppStyle variable is used to color the control. For example, the Fill of the Projects Screen is set to gblAppStyles.Background.Fill, and the imgDeleteMilestone uses gblAppStyles.Icons.DisabledColor as an input to create an SVG (How it creates these SVGs is quite interesting too) that has the right color.

So, this sample app lets you set your own theme colors, adjust specific control properties if needed, and deals with Dark mode and High Contrast. This is so much functionality that will supercharge your UI/UX, but there is still one thing that is not ideal if we use these variables in combination with the standard controls.

The controls still have the default settings if we add a new one. A good solution is to create a template screen that contains all the controls adjusted to the gblAPpStyles variable. But we can also adjust the default settings of those controls. That way we can just add it like we normally do, without making any adjustments.

NOTE from here on it is quite hacky what we will do. ONLY do this in your dev tenant and do NOT implement it in any of your production apps.

✨ Hack The Default Controls

  1. Create a new app

  2. Enable Git source control for your app. You can go back to Challenge 002 to find out how to do this. As you might remember, it asks for your password, but it actually is a private access token that must be given.

  3. Add the snippet below to the OnStart of the app.

  4. Add a Button and set the Fill ColorValue("#ff0000")

  5. Push the changes to GitHub, and close the browser tab.

Set(
    AppTheme,
    {
        palette: {
            themePrimary: "#742774",
            themeLighterAlt: "#f9f3f9",
            themeLighter: "#e9d0e9",
            themeLight: "#d5abd5",
            themeTertiary: "#ab67ab",
            themeSecondary: "#843784",
            themeDarkAlt: "#672367",
            themeDark: "#571e57",
            themeDarker: "#401640",
            neutralLighterAlt: "#faf9f8",
            neutralLighter: "#f3f2f1",
            neutralLight: "#edebe9",
            neutralQuaternaryAlt: "#e1dfdd",
            neutralQuaternary: "#d0d0d0",
            neutralTertiaryAlt: "#c8c6c4",
            neutralTertiary: "#a19f9d",
            neutralSecondary: "#605e5c",
            neutralPrimaryAlt: "#3b3a39",
            neutralPrimary: "#323130",
            neutralDark: "#201f1e",
            black: "#000000",
            white: "#ffffff"
        }
    }
);
Set(
    AppStyles,
    {
        Button: {
            Fill: ColorValue(AppTheme.palette.themePrimary),
            Color: ColorValue(AppTheme.palette.white)
        }
    }
)

It is good to understand how controls are saved. In your repo, you can see the screens are saved as a yaml file. The button you have just created is in it. The properties that are listed here are the properties that are specific t this button. As you know, a button has many more properties, for example, the Color. If nothing is specified here, it will show the theme default value.

In your repo, there is a file called Themes.json. You can use the breadcrumb of the image below to find it in your repo.

The file contains a palette and styles. Sounds familiar right? The palette is where the colors are specified. Search for "controlTemplateName": "button" to find the specifications of the button within the default theme. Color and Fill are specified. The % in the value shows the dynamic value from the palette.

Adjust the value for Color and Fill to AppStyles.Button.Color and AppStyles.Button.Fill. Commit the changes to your repo.

If you open you app in edit mode again, you have to log in with your GitHub credentials. Again, this is the personal access token secret istead if your password. It gets the data from your github repo. You can see that your button has changed to the new theme.

Isn't that amazingly sweet? We can now basically define the default for every control ourselves, and we only have to specify when we want to deviate from our own standard. Be aware that this is at your own risk, and that there are many controls and properties to templatize. However, if you put in the effort, your app development will become so much easier.

That's it for this challenge, and thus for the 2022 Canvas Apps 🏕️ summer camp 🏕️. I hope you liked the series. Now we can all look forward to the rainy season. Yeey...

🎒 Key takeaways

👉🏻 UI/UX is about removing friction 👉🏻 Theming is a core function of the Creator Kit 👉🏻 A combination of AppColors and AppStyles gives us all the flexibility we could ask for 👉🏻 We can make the default controls adapt to the AppStyles variable