Article

Supporting Dark Mode in Your Interface

Adopt a dark appearance in addition to the standard light appearance.

Overview

In macOS 10.14, users can choose to adopt a system-wide light or dark appearance. The light appearance derives from the original Aqua interface of macOS. The dark appearance, known as Dark Mode, is a system-wide implementation of an appearance that many apps already adopt.

A system displaying a dark appearance in the Calendar app and in the documentation viewer of Xcode.

The choice of whether to enable a light or dark appearance is an aesthetic one for most users, and might not relate to ambient lighting conditions. Apps should support both appearances, but there may be reasons to support one appearance over another. For example, even in Dark Mode, you may want to adopt a light background for printed content.

Before making any changes to your app, always turn on Dark Mode and see how your app responds. AppKit does a lot of work for you, such as automatically updating standard views and controls to match the system appearance. For custom views, there are ways to implement your views that allow them to adapt naturally to both light and dark appearances as well. For example, if you use the recommended AppKit colors, those colors update automatically. If you hard-code colors and images in your custom views, you’ll need to make changes to support both light and dark appearances.

Enabling Dark Mode in the General pane of the System Preferences app.

Choose Colors that Adapt

The light and dark appearances use very different color palettes. If your app uses hard-coded color values, those values may be inappropriate when switching to a dark appearance. However, there are two simple ways to address color issues:

  • Use a semantic color defined by the NSColor class. Semantic colors let you specify colors based on their intended usage, rather than on the actual color. Examples include labelColor, controlColor, and controlBackgroundColor. For a complete list, see NSColor.

  • Add custom colors to your asset catalog.

Use semantic colors when mixing your custom views and controls with standard AppKit views. Semantic colors ensure that your custom views have a similar appearance to the other views in your interface.

When you can’t use a semantic color, specify your color in an asset catalog. Use the appearance pop-up menu to configure whether your Color Set asset has custom slots for light and dark appearances. Colors in the Light Appearance and Dark Appearance slots are available only on macOS 10.14 and later. To specify the color for earlier versions of macOS, use the Any Appearance slot.

For color assets, use the appearance options to add color slots for light and dark content.

To load a color value from an asset catalog, use code like:

let aColor = NSColor(named: NSColor.Name("customControlColor"))

You do not have to recreate asset-based color objects when the current appearance changes. Each time you set the fill or stroke color for drawing, NSColor applies the appropriate color value from the asset catalog. Similarly, you do not need to recreate any of the predefined color objects that AppKit provides, such as labelColor. However, you do need to recreate color objects containing hard-coded component values, if those colors would be affected by appearance changes.

Create Images for Dark Appearances

Check the images in your interface to make sure they look good in both light and dark appearances. You can include images in buttons, image views, or custom views and controls. If an image is difficult to see when changing appearances, provide a new image asset that looks good in the other appearance.

For information on how to configure images for both light and dark interfaces, see Providing Images for Different Appearances.

Update Custom Views Using Established Methods

When the current appearance changes, AppKit automatically asks each window and view to redraw itself. For custom views, AppKit calls one or more of the following methods:

As long as you update your view from one of these methods, your view will have a chance to update itself when the appearance changes. Be aware that you might need to perform tasks in these methods that you might have previously performed elsewhere. For example, instead of setting the background color of your view’s layer at creation time, you now need to put that code in your view’s updateLayer method, as shown in Listing 2. Although the NSColor object in the following example updates automatically, the background color of the layer does not. As a result, you need to assign an updated CGColorRef explicitly.

override func updateLayer() {
   self.layer?.backgroundColor = NSColor.textBackgroundColor.cgColor

   // Other updates.
}

Choose Visual Effect Materials Based on the Intended Usage

You use NSVisualEffectView objects to give depth to your interface. Typically, you use a visual effect view as a background view, placing other content such as labels and controls inside it. You also choose a material for your visual effect view. Materials determine the amount of translucency and blending imparted by the visual effect view onto your content.

Always choose materials based on their defined usage, not based on how those materials look. For example, when using a visual effect view as the background of a popover, choose the NSVisualEffectMaterialPopover material. Materials adapt automatically to the system appearance, including light and dark changes. You do not have to change materials manually.

For more information about using visual effect views in your interface, see NSVisualEffectView.

Opt Out When Appropriate

Whether you adopt both light and dark appearances is an aesthetic choice; however, you are encouraged to support both. If your design requires opting out of one appearance, you can tell AppKit which appearance you want to use. For example, you might adopt a dark appearance for your app at all times because it highlights your content better. You might also adopt a light appearance for user documents or printable content.

You can configure all or part of your interface to opt out of a specific appearance. You can also adopt a specific appearance for your entire app. For more information about how to make these changes, see Choosing a Specific Appearance for Your App.

Avoid Expensive Tasks During Appearance Transitions

When the system appearance changes, AppKit updates your interface to the new appearance. Most of these changes happen automatically, but some might result in calls to your code. For example, if you create images using a drawing handler, AppKit calls your handler block in response to an appearance change. Apps can also use key-value observing (KVO) to monitor changes to the effectiveAppearance property of a view or window and make custom changes.

When responding to an appearance change, update your interface as quickly as possible. Do not perform tasks that are unrelated to the appearance change. AppKit creates transition animations between old and new appearances, but aborts those animations if your app takes too long to finish its updates.

Topics

Appearance Support

Providing Images for Different Appearances

Supply image resources that work for light and dark appearances and for high contrast environments.

Choosing a Specific Appearance for Your App

Override the system appearance when needed to implement the interface style that makes sense for your app.

See Also

User Interface

Views and Controls

Present and define the interactions for your content onscreen.

View Management

Manage your user interface, including the size and position of views in a window.

Menus, Cursors, and the Dock

Implement menus and cursors to facilitate interactions with your app, and use your app's Dock tile to convey updated information.

Windows, Panels, and Screens

Organize your view hierarchies and facilitate their display onscreen.

Touch Bar

Display interactive content and controls in the Touch Bar.

Animation

Animate your views and other content to create a more engaging experience for users.

Sound, Speech, and Haptics

Play sounds and haptic feedback, and incorporate speech recognition and synthesis into your interface.