Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Spec] (iOS 13) Dark Mode & Semantic Colors #7304

Closed
jfversluis opened this issue Aug 28, 2019 · 24 comments
Closed

[Spec] (iOS 13) Dark Mode & Semantic Colors #7304

jfversluis opened this issue Aug 28, 2019 · 24 comments

Comments

@jfversluis
Copy link
Member

(iOS 13) Dark Mode & Semantic Colors

In preparation to the long awaited Dark Mode that is added by Apple to iOS 13, there are a couple of new APIs we can leverage and should surface to Xamarin.Forms.

hero

Another thing we should consider is that there is a high contrast mode for accessibility purposes. While this mode has been available to iOS for a long time, there is not as much attention for it as Dark Mode. With Dark Mode introduced, that brings us to having four options:

  1. Default (being "Light Mode")
  2. Default High Contrast
  3. Dark Mode
  4. Dark Mode High Contrast

That's why it is also important to leverage the default functionality of iOS as much as possible. By using the color APIs provided by Apple as much as possible, we will get support for all these different modes for free.

Requirements

1. Built and released with Xcode 11 (or higher)

If you are using previous versions of Xcode, the app will always have its normal appearance, even if the user has Dark Mode enable on their device

2. Run on an iOS 13 device

Only devices with iOS 13 and up support Dark Mode. You cannot enforce Dark Mode on pre-iOS 13 versions. At least, not without your own custom code to achieve that.

If these two requirements are fulfilled, your app will automatically (try) to switch to Dark Mode whenever the user has enabled that on OS level.

However, if your app is not yet properly prepared because you might have used hard-coded colors, things might start to look bad. Since we are working hard on making clear that Xamarin.Forms UIs can be beautiful, let's make sure that doesn't happen to us.

API

Dynamic Colors

In iOS 13 dynamic colors are added. These colors will be different, depending on what mode is enabled for your app. For instance, if we look at SystemBlue this would be 0, 122, 255 (RGB) in default/light mode and in dark mode the RGB values shift to 10, 132, 255. A subtle difference. Again, if we think about high-contrast mode, this is automatically taken into account as well when using the System prefixed colors. The high-contract scheme also has two separate values for default and Dark Mode.

image
Top: system colors in "Light Mode" bottom: system colors in Dark Mode

Color

System Colors

API Description
Color.SystemBlue Dynamic color for blue
Color.SystemGray Dynamic color for gray
Color.SystemGreen Dynamic color for green
Color.SystemIndigo Dynamic color for indigo
Color.SystemOrange Gets or sets Dynamic color for orange
Color.SystemPink Dynamic color for pink
Color.SystemPurple Dynamic color for purple
Color.SystemRed Dynamic color for red
Color.SystemTeal Dynamic color for teal
Color.SystemYellow Dynamic color for yellow

Note: I have just taken the Apple naming convention here. We might also consider an alternative. Maybe: SemanticBlue, SemanticGray, etc.

Since there will also be the possibility to define custom colors, although discouraged, we also need an API to access those.

API Description
Color.GetByName(string name) Retrieves the developer-defined semantic color by name

6 Shades of Gray

In addition, the iOS 13 APIs provide you with a range of six opaque gray colors which you can use in rare cases where translucency doesn't work well. The names for these are very creative.

API Description
Color.SystemGray Dynamic color for gray (same as above)
Color.SystemGray2 Dynamic color for gray2
Color.SystemGray3 Dynamic color for gray3
Color.SystemGray4 Dynamic color for gray4
Color.SystemGray5 Dynamic color for gray5
Color.SystemGray6 Dynamic color for gray6

Control Colors

Lastly, there are some colors that now specifically target the use of a certain control.

API Description
Color.Label Text label that contains primary content
Color.SecondaryLabel Text label that contains secondary content
Color.TertiaryLabel Text label that contains tertiary content
Color.QuaternaryLabel Text label that contains quaternary content
Color.PlaceholderText Placeholder text in controls or text views
Color.Separator Separator that allows some underlying content to be visible
Color.OpaqueSeparator Separator that doesn't allow any underlying content to be visible
Color.Link Text that functions as a link

Detect Dark Mode

DeviceInfo

In the Device.DeviceInfo we add a property to be able to see if Dark Mode is enabled

API Description
IsDarkModeEnabled Is Dark Mode enabled on this device

Page

DarkModeChanged Event

To be able to respond to changes we expose this event on all pages. While this is detectable per UIView (XF: VisualElement) I deemed it only necessary to detect it on Page level.

API Description
DarkModeChanged(DarkModeEventArgs args) Event that is fired whenever Dark Mode is enabled/disabled
public class DarkModeEventArgs : EventArgs
{
    public bool IsDarkModeEnabled { get; }
}

Force Appearance Mode

There is also the possibility to override the configured appearance mode. While this is overrideable per UIView (XF: VisualElement) I deemed it only necessary to detect it on Page level. Subviews will inherit automatically.

API Description
ForcedAppearanceMode Property that lets the developer set a fixed appearance mode

To support this, we would need to have an enum to be able to select the appearance mode we want to force

public enum AppearanceMode
{
    DarkMode,
    LightMode
}

VisualElementRenderer

This part is per definition iOS specific. In addition to all of the above, Apple also added new values to the UIBlurEffect.Styles. In addition to the values that are available since iOS 8 (None, ExtraLight, Light & Dark), values are added (systemUltraThinMaterial, systemThinMaterial, systemMaterial & systemThickMaterial) that will also adapt automatically to the appearance mode. In Xamarin.Forms we have a platform specific to set these, so we need to update this to be in line with the new APIs.

public enum BlurEffectStyle
{
    None,
    /// <summary>
    /// Available in iOS 8.0 and later.
    /// </summary>
    ExtraLight,
    /// <summary>
    /// Available in iOS 8.0 and later.
    /// </summary>
    Light,
    /// <summary>
    /// Available in iOS 8.0 and later.
    /// </summary>
    Dark,
    /// <summary>
    /// Available in iOS 13.0 and later.
    /// </summary>
    SystemUltraThinMaterial,
    /// <summary>
    /// Available in iOS 13.0 and later.
    /// </summary>
    SystemThinMaterial,
    /// <summary>
    /// Available in iOS 13.0 and later.
    /// </summary>
    SystemMaterial,
    /// <summary>
    /// Available in iOS 13.0 and later.
    /// </summary>
    SystemThickMaterial,
}

Scenarios

You're laying in bed, the sun dropped behind the horizon. Crickets are softly chirping outside. You hear them, but they don't really catch your attention. You are already halfway asleep when your phone vibrates. It's a notification. The notification is spawned by your favorite app, built with your favorite framework: Xamarin.Forms.

While you subconsciously appreciate the beauty of Dark Mode on your brand new iOS 13 installation, you tap the notification and the app opens. Suddenly you can't see. You are blinded by a light as bright as the sun. Your phone drops onto your face to then drop even further to the ground. You grasp for air while quickly considering the options: aliens have finally invaded? Did your smart lights get hacked? Was your mom right and did your sight finally give out because of watching a screen too much?

Then it starts to sink in... This is the one app that does not support Dark Mode yet. Let's make sure this scenario will not become reality. Using Apple's words: “You really don’t want to be that one light appearance that’s stuck in dark appearance”.

Backward Compatibility

Since this adds only new APIs backwards compatibility should not be an issue.

If a user wants to opt-out and/or force a certain appearance, they could set the new UIUserInterfaceStyle key in the info.plist file. The value should then be light or dark. This will make the app always use one or the other and disregards the end-users setting. Of course, this is not recommended.

Difficulty : Low - Medium

It's mostly just opening up new API calls and make them accessible through the Xamarin.Forms layer. There might be some challenge in implementing the "detect which mode is on" functionality, since iOS does not have a clear-cut API for that.

Additional extra credit

App Icon

To support Dark Mode to its full extend, we might want to consider opening up the API that Apple has in place to be able to replace the App Icon on the Springboard. That way users can easily implement logic to offer different app icons to the end-users and have them choose between a light and dark themed icon

Mac OS support

While this spec is focussed towards iOS, let's not forget that Mac OS has a Dark Mode these days as well. It should be relatively easy to add support for Mac OS as well.

Android Support

Since Android is planning a similar dark mode (called dark theme) it might be good to plan to take that already into account. Since the infrastructure implemented for this already needs to reach out to the specific platform, it should not be too hard to map the Android dark theme specifics onto this new APIs.

Linked issue(s): #6483

@davidortinau
Copy link
Contributor

davidortinau commented Aug 28, 2019

IsDarkModeEnabled
DarkModeChanged

Should we be so specific to "dark mode"? I would feel more comfortable with more flexible naming like "AppearanceMode" or "UserInterfaceStyle".

How Do I...

There are 2 things I want to control for each mode: Images and Colors.

Images

Typically we would add our images to the platform designated folder, and reference them in Xamarin.Forms by common name.

<Images Source="Logo"/>

How do I make sure the right image is used per appearance mode? In iOS I can use the asset catalog to set images per mode, however this isn't how Xamarin.Forms styles things really.

  1. If I put images in the Asset.xcatalog per mode, I expect to see them in Xamarin.Forms. It doesn't appear this works right now.

  2. If I manage my own images, I want a way to specify them using source per mode.

<Image Source="{OnMode Dark=LogoOnWhite, Default=Logo}"/>

Do we define image source via styles?

Colors

Typically specified in styles.

<Color x:Key="myTitleColor">#FF36D1DC</Color>

In Assets.xcatalog this is as easy as a ColorSet. How might I do this in Xamarin.Forms?

<ColorSet x:Key="myTitleColor">
    <OnMode>
        <On Mode="Light">#FF36D1DC</On>
        <On Mode="Dark">#FFFFFFM</On>
        <On Mode="Default">#FF36D1DC</On>
    </OnMode>
</ColorSet>

Is now the time to introduce ThemeResource?

https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/xaml-theme-resources

@samhouts samhouts removed this from New in Triage Aug 28, 2019
@stevechadbourne
Copy link

Would it be possible to add Windows 10 support as well? There is a default app mode with Light and Dark options in settings.

@davidortinau
Copy link
Contributor

@stevechadbourne agree, that's what I'm referring to with ThemeResource. I don't know though if this would be compatible with our current implementation of resources. I should think it would work, but we need @StephaneDelcroix to weigh in.

@jfversluis
Copy link
Member Author

Should we be so specific to "dark mode"?

Agreed. I was kind of conflicted with this. In retrospect I see that I'm already using the AppearanceMode myself for the last bits. I guess it makes more sense that way, especially when we mix in the Windows stuff.

So, then it would be more like

DeviceInfo

In the Device.DeviceInfo we add a property to be able to see if Dark Mode is enabled

API Description
CurrentAppearanceMode What is the actual appearance currently for this device

Page

DarkModeChanged Event

To be able to respond to changes we expose this event on all pages. While this is detectable per UIView (XF: VisualElement) I deemed it only necessary to detect it on Page level.

API Description
AppearanceModeChanged(AppearanceModeChangedEventArgs args) Event that is fired whenever the appearance changed
public class AppearanceModeChangedEventArgs : EventArgs
{
    public AppearanceMode OldAppearanceMode { get; }
    public AppearanceMode NewAppearanceMode { get; }
}

For Images and Colors: both excellent points. To be honest I would expect that to just work whenever Xamarin.iOS has support for the new assets infrastructure but maybe that is too naive.

The things you are proposing are very good, but I think are more targeted towards expanding the XF styles in general?

@KSemenenko
Copy link
Contributor

Maybe it makes sense to add a color change to the StatusBar? #7314

@samhouts samhouts added this to Needs Design Review in Enhancements Aug 29, 2019
@dansiegel
Copy link
Contributor

@jfversluis you started this talking about also providing flexibility for High Contrast. The spec as outlined though is focused entirely on Dark Mode.

A couple of things here... I actually like where @davidortinau is going with the OnMode. I do think there is probably a temptation to make this some sort of enum like:

public enum DisplayMode
{
    Default,
    Light,
    LightHighContrast,
    Dark,
    DarkHighContrast
}

I would raise the question though, as long as the work is being done should this perhaps support user defined modes? What I want to ignore the system and supply my own mode via some settings page or something?

@ginobbs
Copy link

ginobbs commented Sep 20, 2019

Is there a similar issue for Android Dark Theme as I was not able to find one? "Android Support" is mentioned at the bottom of the specs but nothing more concrete. We would expect that the "semantic colors" and everything else related would be working on Android as well.

@jrahma
Copy link

jrahma commented Sep 21, 2019

I think this should be a property for the ContentPage, something like this:

DarkMode="{Enabled / Disabled / Device}"

Where:

  • Enabled is DarkMode always enabled
  • Disabled is DarkMode always disabled
  • Device is DarkMode depends on the device DarkMode settings

@jamesmontemagno
Copy link
Contributor

We are looking to integrate a new DeviceInfo API into Xamarin.Essentials: xamarin/Essentials#923 Putting it here as an FYI

@jfversluis
Copy link
Member Author

All good suggestions everyone! Sorry for the radio silence on this.

@dansiegel great point! Although it is written primarily around Dark Mode, high contrast is definitely part of this. Actually later today (13-14 PST), for the people seeing this in time, there will be a session on Twitch where we go through the different Dark Mode/Themes and have a discussion about possible implementations. If you want to weigh in you're more than welcome!

In any case, there will be some movement after that stream and discussion probably :)

@justadaniel
Copy link

I think it wouldn't be a bad idea to add UWP into the mix since Windows has had a dark/light theme for a while now. Adding that for parity would be pretty slick

@jfversluis
Copy link
Member Author

@justadaniel I'm working on a new/additional proposal to attack this problem. We are discussing it internally for now. We should be able to share something soon :)

Thanks for your input!

@justadaniel
Copy link

@jfversluis is there a link for the twitch stream where it was discussed? I would like to watch it if it's available.

Thanks for taking the time to discuss possible solutions. It really helps us developers out in the long run. Happy Holidays y'all!

@jfversluis
Copy link
Member Author

@justadaniel definitely! You can find it here. On that channel are also the other recordings of that week.

I'm still working on a solution for this. Basically we have two ways to go and we need to decide which we'll use. When we figure it out, I'll update you.

Happy holidays!

@Mikilll94
Copy link

Mikilll94 commented Jan 14, 2020

@jfversluis
It's great that Xamarin.Forms will open up iOS dark mode API.

But first, maybe you will fix this bug #8495? It is closely related to dark mode on iOS 13.

@jfversluis
Copy link
Member Author

Definitely on the list for the whole Dark Mode/OS theming support. Thanks!

@dotMorten
Copy link
Contributor

To be able to respond to changes we expose this event on all pages. While this is detectable per UIView (XF: VisualElement) I deemed it only necessary to detect it on Page level.

For control developers it would be useful to have at the view level as well, as we don't have access to the page where someone might place the control.

@dotMorten
Copy link
Contributor

Regarding iOS, Mac and Android support mentioned: You left out UWP which have had darkmode since the beginning.

@KennyDizi
Copy link

love this: Image Source="{OnMode Dark=LogoOnWhite, Default=Logo}"

@dotMorten
Copy link
Contributor

@xamarindevelopervietnam UWP has a way to automatically invert monochrome images in the AppBarButtons, which means you don't have to deal with supplying two resources. Perhaps in some scenarios this feature could be expanded to across the board?

@KennyDizi
Copy link

thanks @dotMorten for the update, I don't work with UWP for along time ago.

@dotMorten
Copy link
Contributor

I'm actually a bit surprised that the following plain XAML will render Black text on a black background when in darkmode, making a Xamrain.Forms app basically unusable in dark mode:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="App1.MainPage">


    <ListView x:Name="list">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <Label Text="{Binding}" />
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

</ContentPage>

I can't really set the color to White because that wouldn't work in light mode, and I can't set it to Black because that doesn't work in darkmode. I'd expected that when I don't set a color, the default text color matches the label color in iOS.

@samhouts samhouts moved this from Needs Design Review to Needs Specification in Enhancements Feb 12, 2020
@samhouts samhouts moved this from Needs Design Review to Under consideration-High Interest in Enhancements Feb 12, 2020
@samhouts samhouts moved this from Under consideration-High Interest to Ready for Implementation in Enhancements Feb 12, 2020
@Transis-Felipe
Copy link
Contributor

Transis-Felipe commented Feb 29, 2020

Lots of controls looks weird with dark background on Android. E. G. Entry, switch, progressbar.

@jfversluis
Copy link
Member Author

Thank you for the feedback everyone!

To have a more consistent cross-platform story I came up with #9804 and closing this in favor of that one. I think I addressed most of the feedback from here, including the latest things, i.e. having control on view level.

More feedback of course more than welcome, but I'm starting work on this, so be quick! ;)

Enhancements automation moved this from Ready for Implementation to Closed Mar 3, 2020
@samhouts samhouts removed this from Closed in Enhancements May 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests