Rendering Rich Text in Dark Mode

Yesterday, I demonstrated how, six months after Apple released the first beta version of macOS Mojave, neither macOS nor its apps are consistent in the rendering and handling of Rich Text across appearance modes. Apps like the version of TextEdit which shipped in macOS 10.14.2, and features such as QuickLook thumbnails and previews, must also be consistent with other parts of macOS.

The requirement

The great majority of Rich Text is set in default foreground and background colours, which the user does not explicity specify. When working in Light Mode, users expect this to be rendered in a dark colour, such as black, on a light coloured background, such as white. If a Rich Text editor or reader changes the background for Dark Mode, then the user expects the colour of that text to change, making it bimodal.

Some users, in some passages of Rich Text, may choose to set non-default colours for text and the background on which that passage is displayed. As with colour settings in drawings, designs and other graphical content, where the user has set either or both of the foreground and background colours, they expect those settings to be respected, making such text unimodal.

Principles

Bimodal text should be rendered in a colour appropriate to the default background colour set as the appearance. When the background is dark, bimodal text should be light; when the background is light, bimodal text should be dark.

The exact light and dark colours used to display bimodal text can vary, and can be adjusted by the user to suit their preference. Where possible and appropriate, some users like the option of lower contrast, rather than near-black and near-white.

Unimodal text in which the user has chosen a specific colour for either or both the foreground and background should always be displayed in the colour set by the user. The notable exception to this is where the text colour has been set to black, but the background has been left in the default; in those circumstances, some implementations may wish to treat that text as being bimodal.

Wherever possible, if a user has set neither the foreground nor background colour but left those to defaults, text should be treated as being bimodal.

Implementation

In its handling of Attributed Text (NSAttributedText) and RTF, macOS Mojave already uses an extension to the RTF standard in the form of expanded colour tables using the control word \expandedcolortbl which can be and are used to distinguish between unimodal and bimodal text. This doesn’t appear to have been documented by Apple, whose only documentation of extensions to RTF has been archived, unmaintained, and without replacement.

The standard colour table, introduced by the control word \colortbl, sets colours to be used in the display of Rich Text where there is only a single appearance mode. The first entry in the standard colour table is usually empty, consisting of just its delimiting semicolon. This indicates the default colour, colour 0, or ‘auto’ colour.

One simple way to allow for the explicit specification of bimodal text is to use matching \colortbl and \expandedcolortbl entries such as:
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0\cname textColor;}

then to set bimodal text as, e.g.
\cf2 This is bimodal

Apps which only support a single appearance can ignore the \expandedcolortbl and set that text in black using the \colortbl values. For those which do support two (or more) appearance modes, they can refer instead to the \expandedcolortbl, in which the Light Mode colour is specified as being black, and that colour is named as textColor, establishing that it as bimodal.

This leaves the problem of Rich Text which doesn’t include an \expandedcolortbl being rendered by an app which can display text in two or more appearance modes. In many cases, text which has been left in default colour on a background of default colour, such as
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
\cf0 This is default

could reasonably be treated as bimodal. That is not current behaviour in macOS 10.14.2, though.

In contrast, text with either or both colours set to an explicitly-specified colour, such as
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
\cb1 \cf2 This is unimodal

should be set in the specified colours, here black on white, regardless of the prevailing appearance. That is current behaviour in most of macOS 10.14.2, although apps such as TextEdit in that version do not comply, but insist on inverting the colours set by the user.

The only ambiguous situation is when the foreground colour is set to black, but the background left in the default colour, as in
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
\cf2 This is steering into danger

which relies on the background colour being cb1. Implementations may there find it better to treat that text as being bimodal. In NSAttributedText, for example, this can be detected by looking for colour attributes which specify black text on the default background colour.

A Rich Text editor which supports Dark Mode should always write an \expandedcolortbl including a named textColor to ensure that there are no ambiguities with respect to black or white text on a default background colour.

Changes in the choice of colours would logically be implemented within TextKit to apply to the drawing of attributed text wherever it is used.

(Original version 9 December 2018.)