Make your macOS app themable

Check how to effortless add theming support to your macOS application with the new ThemeKit framework.

Paw making use of ThemeKit

I have been a theme-lover for many years 🌝🌚. I used to spend hours browsing and downloading themes for window managers in the past (mostly ), and even for on . With time, most Operating Systems either provided some theming API, or become theme-friendly in some way. Well, that — unfortunately — excludes which, historically, never played really well with theming 😞 (1). For people who love to customize its (like me), not having a good support for theming on the OS is quite disappointing…

After said goodbye to skeuomorphism with , a lot of things changed UI-wise. The OS partially embraced the “Age of Minimalism”, adopting the much loved and hated flattish iOS 7 inspired design. Cool thing is that was keen enough to add a “Use dark menu bar and Dock” setting! This was a good start… but not enough: if you work at night, you may turn that setting on, you may even use f.lux do reduce the brightness by warming up the colours, but you will still have those bright grey applications 😕 (which is actually where your eyes are focused on).

Disclaimer: I have co-created Flavours in the past, which adds theming capabilities at the OS level (thus, for apps). That gave me a really deep insight of how things work and how hard it can be to theme on .

While working with the team — Paw is a great (or, the greatest) API tool for Mac — we encountered these same issues again, so we wanted to add some theming capabilities to in order to make it more pleasant to work with at night There was already some Mac apps which added support for dark mode, but unfortunately, there was no open source framework to do this easily, without having to re-invent the wheel. So this is was our motivation for building : we developed an easy to use framework to add theming support to apps, and shared it with the world!

is a lightweight theming library completely written in that provides theming capabilities to both Swift 3.2+ and Objective-C applications.

Dark Theme for Everyone!

At its simplest and minimal usage, applications can be programmatically themed with a light/dark theme in a single line command:

So, just by bundling and using one line of code, your application would already have a basic dark theme!

Light / Dark Theme

Of course, you probably want finer grain about the theme details, so let’s move on…

Theme-aware Assets

offers the ability to have colours, gradients, and images that dynamically change depending on the applied theme.

As an example, suppose that a project defines a ThemeColor.brandColor color. Because it is a theme-aware color, it will resolve to different colours at runtime, depending on the selected theme:

  • Light Theme: ThemeColor.brandColor = NSColor.blue
  • Dark Theme: ThemeColor.brandColor = NSColor.white

We can then use this ThemeColor.brandColor for changing control colours, to draw on views, create custom controls, etc, and its color will automatically change with the theme.

Similarly, ThemeGradient and ThemeImage provide theme-aware gradients and images, respectively.

also allows NSColor named colours (e.g., NSColor.labelColor) to be overridden so that the system provided colours can be replaced with theme-aware colours. These can be defined on the ThemeColor subclass extension, which will override the system ones, providing theme-aware colours (e.g., defining a ThemeColor.labelColor will override NSColor.labelColorThemeColor is a subclass of NSColor).

Bundled Themes

is bundled with 3 “blank” themes:

  • LightTheme: (default) light appearance
  • DarkTheme: dark appearance
  • SystemTheme: automatically alternates between LightTheme and DarkTheme, reacting to user setting in “System Preferences > General > Appearance”

These bundled themes only differ in the window appearance (light or dark), so absolutely no colours are changed or than that. It is up to the developer to decide what can or not be themed.

Creating New Themes

If you are going to define theme-aware assets (colours, gradients and/or images), before start creating themes for your application, you must first define the application themable assets.

We do this by extending ThemeColor, ThemeGradient, and/or ThemeImage as below. For this example, we will only be adding three themable assets: two colours and an image.

Defining theme assets

As a bonus, code completion is now working for ThemeColor and ThemeImage classes 🙂

And now, let’s provide two new themes:

  • a light theme, natively written in (could have been done in as well)
  • a dark theme, defined on a plain text file (a .theme file).

Defining a native theme

Defining a user theme

Of course, the great advantage of user .theme files is that these are just plain text files! This means that not only you can add theming support to your application, but you can also give your users the option and power to design custom themes!

Additionally, if you have a user theme (.theme) applied, automatically reloads and applies it when it changes on disk:

Live editing of `.theme` files

Using our theme-aware assets

On our view controller, let’s change the image and colour of NSImageView and NSTextField, respectively:

Putting it all together

Now we just need to setup and initialize in applicationWillFinishLoading():

Our Sample App

Going further…

If you plan to add theming capabilities to your application, you should definitively consider giving a try!

Check the ThemeKit project page where you can find all the details about the framework: installation, configuration, customization, documentation, etc. You can also download a pre-built binary of the Demo project which showcases some of the features.

ThemeKit Demo.app

Useful links & material

(1) Curiosity: While theming and never played well, the same wasn’t true for its predecessor, the . At the time, bundled Mac OS 8 and 9 with Appearance Manager, a native UI theming API. And on top of that, there was a commercial and super popular app: Kaleidoscope! Later, on , most popular theming solutions were ShapeShifter (10.2–10.5) and Flavours (10.7–10.10), but all of them needed to hack the OS in order to accomplish the goal.

Fun, positive and open-minded. Love to laugh! Co-founded @interactolabs, former Paw @luckymarmot contributor — http://nunogrilo.com