Skip to content

No Yes/No, yes?

Tuesday, 13 September 2022 | Friedrich Kossebau


How some evening supermarket shopping is triggering some API work…

Human Mind vs. Machine Mind

Some time ago I ran into a variant of a self-service checkout system in a supermarket which, when asking for applying the data collection identity card, used a dialog with the button options “Yes” & “No”. Being privacy-positive, my thoughts were, yes, I want to keep my data private, and was about to press the “Yes” button. Only to check once more and find that the question actually was “Do you want to use our card?”. Which made me wonder why in the year 2022 new systems are developed that apply that old pattern of “Yes” & “No” replies. And reminded me that also in newer software made in the KDE community I had seen new appearances of that scheme. Had it not been found to be inferior, from what I had seen by-passing in the HMI field?

What Does The Human Interface Guideline Say

Let’s see what in 2022 the guidelines for some prominent UI systems recommend for buttons in dialogs.

Apple’s Human Interface Guidelines for Alerts (dialogs) about text to use on buttons:

Aim for a one- or two-word title that describes the result of selecting the button. Prefer verbs and verb phrases that relate directly to the alert text — for example, “View All,” “Reply,” or “Ignore.” In informational alerts only, you can use “OK” for acceptance, avoiding “Yes” and “No.” Always use “Cancel” to title a button that cancels the alert’s action.

Google’s Material guidelines on behavior of Alert dialogs:

Don’t use action text that fails to indicate what the selection will do. “Cancel” and “Delete” better indicate what will occur in this dialog.
[ed.: comment on a “Don’t” example using “NO”/”YES” buttons]

Microsoft’s Fluent Design System guidelines on buttons of Dialog controls:

Use specific responses to the main instruction or content as button text. An example is, “Do you want to allow AppName to access your location?”, followed by “Allow” and “Block” buttons. Specific responses can be understood more quickly, resulting in efficient decision making.

And respective recommendations can be also found in guidelines of FLOSS projects:

Haiku’s Human Interface Guidelines hold for Alert Windows this:

Avoid Yes / No button labels. It is much better to use the name of the action in the label, such as Save Changes / Discard Changes. Only in very rare cases are Yes / No labels the best choice.

Also KDE’s Human Interface Guidelines state on Modal Message Dialog:

Buttons should clearly indicate the available options using action verbs (“Delete”, “Rename”, “Close”, “Accept”, etc.) and allow the user to make an informed decision even if they have not read the message text. Never use “Yes” and “No” as button titles.

And the GNOME Human Interface Guidelines recommend on Action dialogs:

Label the affirmative button with a specific imperative verb, for example: Save or Print. This is clearer than a generic label like OK or Done.

When looking at older guidelines, e.g. in the NeXTSTEP User Interface Guidelines from November 1993 in the section “Naming Buttons in an Attention Panel” can be read:

When naming buttons in an attention panel, you should label each one clearly with a verb or verb phrase describing the action it performs. The user shouldn’t have to read the text of the attention panel to be able to choose the right button. Thus, generic labels (like Yes and No) aren’t appropriate, as they tend to cause user errors.

And similar, to little surprise, the variant in the OpenStep User Interface Guidelines from September 1996 in its section “Naming the Buttons in an Attention Panel”:

Label each button clearly with a verb or verb phrase that describes its action. Users should be able to read the names of the buttons and choose the right one. They should not need to read other text on the panel. Avoid “generic” labels like Yes and No, because they are not clear and lead to user errors. Avoid using OK unless it is the only button in the attention panel.

So seems the authors of all the HIGs checked agree on avoiding Yes & No. But is that actually founded on data from science & research, or did they just copy from each other?

Backed by Research?

On a quick look I could not find related scientific research reports that could back up the guideline recommendations. But instead I came across research about the related field of designing questionnaires, on the topic of preventing errors in the given answers e.g. due to misunderstandings or lack of concentration. And that seemed to confirm that people gave more correct answers and also felt it simpler to do when the items representing the choice (e.g. a text next to a checkbox) themselves had clear unique references to the choice instead of being abstract items whose meaning only could be known by the assignment to a choice in the question itself. Abstract items being things like colors, shapes, positions, numbers or the very Yes & No.

Not seen discussed or even researched, but my theory would be that things are worse even when there is a memory effect and something could mean the opposite in other similar choices.

Own experience with soda machines or coffee machines would confirm that, less mistakes remembered when pushing a button with the image of the wanted drink on it over entering a number on a dial to express the selection. Even more when the motivation for a drink was temporary brain insufficiency 😉

(If a reader has some pointer to related public papers, happy to add here).

API-Driven UI

By personal experience a lot of software is produced patch-by-patch, feature-by-feature, idea-by-idea. Often by people who at most learned how to write syntax-conforming code. And to be efficient, typically things are developed using resources which are available, e.g. deploying existing standard libraries. Thus instead of UX engineers designing HIG conforming UI stories directing the implementation, as theory would suggest, it is the API and any documentation around it of existing UI components libraries.

Legacy API Rooting for Yes & No

One might remember more “Yes” & “No” buttons in older software. One reason might be that those texts (and their localized variants) need less horizontal space on the screen, something to consider for sure in low-dpi times. But then also still in hi-dpi times, when there are other constraints requiring very short texts to have it fit on the display.

Another reason was that it saves resources in the own software, if one just has to pass a flag to denote a set of predefined standard buttons with their texts and possibly icons instead of having to maintain and carry around all the data in the own executable and then pass it over at runtime. And such flag idea is supported by the API of legacy libraries.

The classic Microsoft Windows Win32 API provides a MessageBox function, to show a modal dialog:

int MessageBox(
  [in, optional] HWND    hWnd,
  [in, optional] LPCTSTR lpText,
  [in, optional] LPCTSTR lpCaption,
  [in]           UINT    uType
);

The argument uType is used with flags to define the buttons to use in the dialog, like MB_YESNO or MB_YESNOCANCEL. Those buttons have hard-coded text . The function return value indicates which button was pressed, like IDYES or IDNO.

This function signature and related definitions have been also carried over to other MS APIs, see e.g. .NET’s System.Windows.Forms.MessageBox with MessageBoxButtons and DialogResult. So still continuing to have developers write “Yes” & “No” options dialogs, despite some HIG from the same company (see above) recommending not to do that.

Borland’s (now Embarcadero) Visual Component Library (VCL) has been following the same spirit offering the function Vcl.Dialogs.MessageDlg:

function MessageDlg(
    const Msg: string;
    DlgType:   TMsgDlgType;
    Buttons:   TMsgDlgButtons;
    HelpCtx:   Longint
): Integer;

The Buttons argument takes flag values like mbYes and mbNo, while the matching returned value are defined by constants like mrYes or mrNo. Latest version 10.4 Sydney at least has an overload with an additional argument CustomButtonCaptions: array of string. But the flags and constants keep the code centered around the concepts of Yes and No.

Another classic UI library is Java’s Swing. It provides a class javax.swing.JOptionPane to use for standard dialog boxes. While the developer can add any custom UI components to the button row, the class itself provides prominently documented convenience API to use predefined button sets by optionType argument values like YES_NO_OPTION or YES_NO_CANCEL_OPTION. Using those flags saves resources needed to come up with own texts and any needed localization, so a developer has motivation to use those.

Then there is Qt. It has had the class QMessageBox (now part of Qt Widgets) providing a modal dialog. That has an enum with values like QMessageBox::Yes and QMessageBox::No, which is used in the class methods to reference predefined button as well as to return the user choice. Static convenience methods like QMessageBox::question() use Yes & No as default arguments, with no option to use custom buttons.

QMessageBox::StandardButton QMessageBox::question(
    QWidget *parent,
    const QString &title,
    const QString &text,
    QMessageBox::StandardButtons buttons = StandardButtons(Yes | No),
    QMessageBox::StandardButton defaultButton = NoButton
);

Custom buttons can be used when manually creating a QMessageBox instance, but the involved flags and constants also here keep the code centered around the concepts of Yes and No.

Again the API was carried over to own newer products, here the QtQuick API. The QML type QtQuick.Dialogs.MessageDialog has a property standardButtons, which takes a flag set for predefined buttons, .like QtQuick.Dialogs.StandardButton.Yes and QtQuick.Dialogs.StandardButton.No.

The sibling type from the QtQuick Controls 2, QtQuick.Controls.Dialog, also has a property standardButtons, which takes a flag set for predefined buttons, here QtQuick.Controls.Dialog.Yes or QtQuick.Controls.Dialog.No. With this type one can customize the button properties in the Component.onCompleted method, but as with QMessageBox the involved flags and constants also here keep the code centered around the concepts of Yes and No.

Looking further, Gtk also has had methods around a Gtk.MessageDialog, with the main instance creation function being:

GtkWidget*
gtk_message_dialog_new (
  GtkWindow* parent,
  GtkDialogFlags flags,
  GtkMessageType type,
  GtkButtonsType buttons,
  const char* message_format,
  ...
);

The argument buttons is used to reference predefined sets of buttons, e.g. GTK_BUTTONS_YES_NO. The dialog will emit a signal when the user chose a button, using values like GTK_RESPONSE_YES or GTK_RESPONSE_NO. One can also only add custom buttons with own text and ids. A note in the documentation for Gtk.ButtonsType hints at least that GTK_BUTTONS_YES_NO is discouraged by GNOME’s HIG.

Oh Yes: KDE Frameworks with Oh-Nos

The KDE Frameworks, a set of extensions around Qt, have quite some APIs designed decades ago. Among them are in the KMessageBox namespace convenience methods around message dialogs, for more feature-rich variants of the static methods of QMessageBox and reflecting the accepted state of art at the time. By the time they have grown into a large set, all encoding their specifics in the method names. But never got adjusted to meet the newer state of the art when it comes to recommended texts on the buttons, including KDE’s own HIG.

Examples are:

ButtonCode
questionYesNo(
    QWidget *parent,
    const QString &text,
    const QString &title = QString(),
    const KGuiItem &buttonYes = KStandardGuiItem::yes(),
    const KGuiItem &buttonNo = KStandardGuiItem::no(),
    const QString &dontAskAgainName = QString(),
    Options options = Notify
);
ButtonCode
questionYesNoCancel(
    QWidget *parent,
    const QString &text,
    const QString &title = QString(),
    const KGuiItem &buttonYes = KStandardGuiItem::yes(),
    const KGuiItem &buttonNo = KStandardGuiItem::no(),
    const KGuiItem &buttonCancel = KStandardGuiItem::cancel(),
    const QString &dontAskAgainName = QString(),
    Options options = Notify
);
ButtonCode
warningYesNo(
    QWidget *parent,
    const QString &text,
    const QString &title = QString(),
    const KGuiItem &buttonYes = KStandardGuiItem::yes(),
    const KGuiItem &buttonNo = KStandardGuiItem::no(),
    const QString &dontAskAgainName = QString(),
    Options options = Options(Notify|Dangerous)
);
ButtonCode
warningYesNoCancel(
    QWidget *parent,
    const QString &text,
    const QString &title = QString(),
    const KGuiItem &buttonYes = KStandardGuiItem::yes(),
    const KGuiItem &buttonNo = KStandardGuiItem::no(),
    const KGuiItem &buttonCancel = KStandardGuiItem::cancel(),
    const QString &dontAskAgainName = QString(),
    Options options = Notify
);

The return type ButtonCode being an ernum with values like Yes and No.

Recent API additions for some more asynchronous variants, though without convenient one-method-call code, by the class KMessageDialog continue the pattern. An instance can be only created by defining its type in the constructor method:

KMessageDialog::KMessageDialog(
    KMessageDialog::Type type,
    const QString &text,
    QWidget *parent = nullptr 
);

Where the argument of enum type KMessageDialog::Type has values like QuestionYesNo, QuestionYesNoCancel, WarningYesNo, or WarningYesNoCancel. To signal the user choice, the class reuses the enum type StandardButton from QDialogButtonBox , with values like QDialogButtonBox::Yes or QDialogButtonBox::No.

Searching the current sources of all KDE software using QWidgets technology for the UI, one can see all that API is heavily used. While many places have seen follow-up work to use custom, action-oriented texts for the buttons, as recommended by the KDE HIG, yet the code itself has to keep using the Yes and No semantics, being part of the API.

The spirit of this API can be again found in the message API available to the KIO workers to request interaction with the user by the front-end:

int KIO::WorkerBase::messageBox(
    const QString &text,
    MessageBoxType type,
    const QString &title = QString(),
    const QString &buttonYes = QString(),
    const QString &buttonNo = QString(),
    const QString &dontAskAgainName = QString() 
);

The argument of enum type MessageBoxType has values like QuestionYesNo, WarningYesNo, or WarningYesNoCancel. Similar patterns in the front-end interface classes.

KDE Frameworks’ QtQuick-based UI library Kirigami with its Kirigami.Dialog and Kirigami.PromptDialog copies the problems of Qt’s QtQuick.Controls.Dialog, having a property standardButtons taking a flag set for predefined buttons by values like QtQuick.Controls.Dialog.Yes or QtQuick.Controls.Dialog.No.

With all this API, and also not a single comment in its documentation about what the KDE HIG has to tell here, and a community reaching out explicitly also to non-educated developers, it is little surprise that even new code written in the KDE community these days uses the discouraged Yes/No dialog pattern.

HIG CPL KF API: TBD ASAP

With the upcoming KDE Frameworks 6 API series around the corner, it would be good to have some substitute API done before, so the HIG-conflicting API could be deprecated still for KF5. And KF6 would only hold API trapping developers into more HIG-conforming UIs.

Some, sadly non-exciting proposals to address this should appear in the next days, both as further blog posts as well as MR with actual code.