Adaptivecards: [UWP] Allow using XAML Brush theme resources for host config color options

Created on 9 Jun 2018  ·  14Comments  ·  Source: microsoft/AdaptiveCards

Today, hosts have to manually change their host config when the theme (accent color) or high contrast changes, since colors are all specified with absolute values in the host config.

    "containerStyles": {
        "default": {
            "foregroundColors": {
                "default": {
                    "default": "#FF333333",
                    "subtle": "#EE333333"
                },
                "accent": {
                    "default": "#FF2E89FC",
                    "subtle": "#882E89FC"
                },

Instead, it would be much easier for UWP hosts if they could just provide a XAML theme resource brush, which automatically updates when themes change, which is what apps use in their own normal UI, like seen below...

<TextBlock Foreground="{ThemeResource AccentBrush}"/>

The host config could be modified to be the following

    "containerStyles": {
        "default": {
            "foregroundColors": {
                "default": {
                    "default": "#FF333333",
                    "subtle": "#EE333333",
                    "uwp": {
                        "default.themeresource": "NormalBrush",
                        "subtle.themeresource": "SubtleBrush"
                    }
                },
                "accent": {
                    "default": "#FF2E89FC",
                    "subtle": "#882E89FC",
                    "uwp": {
                        "default.themeresource": "AccentBrush",
                        "subtle.themeresource": "AccentSubtleBrush"
                    }
                },

And if the uwp.*.themeresource value is specified, the UWP renderer assigns the theme resource rather than the absolute color value.

Area-HostConfig Platform-UWP Proposal Tag-Toasts-V2

All 14 comments

If you are going for platform/renderer-specific exceptions, why not host them inside their own object, a bit ala Visual Studio Code.

This way it'd be easier for other renderers to ignore.

"containerStyles": {
        "default": {
            "foregroundColors": {
                "default": {
                    "default": "#FF333333",
                    "subtle": "#EE333333",
                    "uwp": {
                        "default.themeresource": "NormalBrush",
                        "subtle.themeresource": "SubtleBrush"
                    }
                },
                "accent": {
                    "default": "#FF2E89FC",
                    "subtle": "#882E89FC",
                    "uwp": {
                        "default.themeresource": "AccentBrush",
                        "subtle.themeresource": "AccentSubtleBrush"
                    }
                },

That seems like a good idea ignacionr! I've incorporated your suggestion, thanks!

I'm not sure this needs to be an official, documented feature. It breaks the portability of HostConfig which is the reason HostConfig exists in the first place.

I get why this is convenient and it seems to me that it's OK to do it, but I think it should remain a UWP-renderer-specific feature. Every platform has its own model for theme colors; I don't think we want to end up having all platforms' models explicitly supported in HostConfig.

Also, I would go one step further:

"containerStyles": {
    "default": {
        "foregroundColors": {
            "default": {
                "default": "#FF333333",
                "subtle": "#EE333333",
                "uwp": {
                    "default": "ThemeResource(NormalBrush)",
                    "subtle": "ThemeResource(SubtleBrush)"
                }
            },
            "accent": {
                "default": "#FF2E89FC",
                "subtle": "#882E89FC",
                "uwp": {
                    "default": "ThemeResource(AccentBrush)",
                    "subtle": "ThemeResource(AccentSubtleBrush)"
                }
            },

This way you decouple the property from its format. Otherwise you might end up with multiple "default.XXX" in the long run.

It would be officially documented on the UWP-specific documentation for host config. But yeah, it wouldn't be part of the universal host config.

The .NET object model library's HostConfig object should probably include these though? Because it's certainly plausible that a developer would want to have a ASP.NET server providing a host config for their UWP client, so their ASP.NET server would have to be able to generate a host config that includes these UWP extensions.

We could probably build that with some method extensions or something. But it definitely should be possible (in a strongly-typed way).

Some more thoughts - assuming the feature as described above exists:

  • As a UWP host, I would only use the "uwp" properties, wouldn't I? I wouldn't even set the standard "default" and "emphasis" properties because they are not useful to me
  • By doing that, my HostConfig would be incompatible with other hosts. If another host was to receive my HostConfig it would have to fall back to whatever colors it uses by default
  • If I wanted to make my HostConfig compatible with other hosts, I would actually have to extract the colors from the theme resources and set the standard "default" and "subtle" properties
  • But then of course if I did that it would defeat the purpose of having the "uwp"-specific values in the first place
  • If I were to receive an external HostConfig, it would likely not contain the UWP-specific configuration, and I would have to honor whatever colors are specified in the default properties

So IMO the model you propose (e.g. separating standard and UWP-specific color definitions) is only useful if both the standard and the UWP-specific colors are specified in HostConfig, and that requires extracting colors from theme resources which is what you want to avoid and the reason this issue is opened.

Given that, I just don't think you need to introduce a "uwp"-specific section, just do this:

"containerStyles": {
    "default": {
        "foregroundColors": {
            "default": {
                "default": "ThemeResource(NormalBrush)",
                "subtle": "ThemeResource(SubtleBrush)"
            },
            "accent": {
                "default": "ThemeResource(AccentBrush)",
                "subtle": "ThemeResource(AccentSubtleBrush)"
            },

A host that understands the "ThemeResource(ResourceName)" format will correctly interpret the value, and a host that doesn't will fall back to defaults, exactly the same as if the "uwp"-specific properties existed...

Depends on whether we want the host config to be able to be shared, rather than creating a specific host config for each platform.

There's some similarities in web development... if there's a web browser specific property, websites typically provide that to all clients, even though they could detect which browser is requesting the page and then only return the properties specific to that browser.

I'm open to either option, I think being able to enhance the existing host config for UWP but still have it work on other platforms is nice.

“As a UWP host, I would only use the "uwp" properties, wouldn't I?”

No, more like Visual Studio Code -common properties apply and each rendered would “extend” the container object when they see their name.

So the base and the override complement each other (eg on Vstudio Code you can use tab indentation as a default except for typescript code).

It seems my point was lost. Trying again.

The bottom line is that regardless of the approach chosen, the only way for the HostConfig file to actually be portable is for the theme resources to be resolved, their corresponding colors extracted and put in the standard "default" and "subtle" properties. Short of doing that, another host that attempts to use that HostConfig will fall back to its own default colors.

  • If we believe that is ok, then there is no need to introduce "uwp"-specific properties in the first place.
  • If we believe that is not ok, then the UWP host must extract the resource colors and therefore there is again no need for the "uwp"-specific properties.

Personally, I feel the same. Put it in terms of branding. Imagine a soda company from Atlanta has heavily invested on associating itself with a dark background, red script letters for accented containers. What would be the sense of making an exception and allowing the colors to change dynamically on a specific renderer, just because it supports that feature?

The HostConfig is still portable as I provided it in the original post. The "uwp"-specific properties are purely optional enhancements. The card still renders correctly on Android and iOS, since the normal colors were still provided.

HostConfig authors could certainly choose to create a non-portable host config by not specifying the normal color properties... but they can also choose to create a portable host config by specifying both the normal properties and the enhanced UWP properties.

I'm still not making my point clear.

In your example, where do the standard "default" and "subtle" values come from? Are they the actual colors that correspond to those in the theme resources?

  • If no, then that HostConfig isn't really portable, because it isn't defined in a way that would allow other hosts to properly emulate yours. At that point you might as well leave it to the other host to pick colors, which makes this feature unecessary (if you use the second approach I proposed)
  • If yes, then you are doing the extraction of the theme colors and are injecting them in the HostConfig, and therefore this feature isn't necessary

Maybe an explanation of ThemeResources would help.

ThemeResources are how XAML allows you to have specific color brushes that automatically update with the user's theme.

For example, in Windows, users can pick their own accent color. And they can change it at any point in time. Users can also enter high contrast mode, which might make the "foreground" color white, yellow, green... all depends on what themes the users pick.

Traditionally, apps would have to re-launch or listen to these theme changes and then manually update all of their colors.

That's what they have to do with the UWP Card renderer today too - they have to manually change the host config and re-render every time the theme changes, since those theme colors impact the colors that should be chosen.

In Windows 10, UWP introduced the new ThemeResources. With those, apps can instead just "bind" to the AccentBrush, and then it'll be automatically updated when the user picks a different accent color. Same with high contrast colors. And apps can define their own ThemeResource values too, where they can provide their own color values for each of the "themes" (Normal, Dark, HighContrast-White, HighContrast-Black). Then they can have their own custom colors, but still have them automatically update too.

Therefore, Are they the actual colors that correspond to those in the theme resources? Sort of. The ThemeResource normal theme color would correspond to the host config values. But the ThemeResource allows the colors to change when in different themes, like Dark or HighContrast.

So...

  1. The HostConfig is designed in a way that other hosts can emulate the "Normal" theme color
  2. But other hosts wouldn't be able to replicate the Dark, HighContrast theme colors

It's similar to any platform-specific extension... still functional on other platforms, but takes advantage of some platform-specific functionality to work even better on that given platform.

Ok, so the HostConfig will be specially crafted so that the standard "default" and "subtle" colors are an approximation of the real thing, while the "uwp"-specific properties make it possible to have the color perfectly reflect the current theme.

That works for me.

Just to repeat my original two comments after this long thread:

  1. I would keep this as a renderer-specific feature
  2. I would use the "ThemeResource(NormalBrush)" syntax instead of "default.themeresource"

For 1 I believe we are on the same page and 2 is really up to you in the end given 1.

I think for sure it would be something specific to the UWP rendering of a card, as it relies on being able to insert values provided either by the System's settings/config or resources stored within the App's resources.

So how you implement that in the HostConfig spec, would probably require a prefix. If it is kept to UWP resources only, then a uwp:default type value, which is ignored by other renderers, could be done. Or broaden it out slightly. system:default where each renderer can either provide a value or assign it from system provided colours. iOS has a set of standard colours in its UIKit API I believe. I can't speak to Android's API.

Was this page helpful?
0 / 5 - 0 ratings