Skip to main content

Mutually exclusive

When to use this component

The mutually exclusive component provides the user with a with a way of selecting multiple items or a single opposing value.

How to use this component

This component degrades gracefully if JavaScript is not available to the user. If JavaScript is unavailable it is possible for the user to select all options without inverting the selection therefore further, server-side validation should be performed.

Inputs, radios, checkboxes, date inputs, and textareas all call this macro automatically if they have their mutuallyExclusive parameter set. This will add the necessary checkbox and aria-live markup, and the classes required by the JavaScript that runs the component. Where you are combining input components such as in the duration example you must call this macro and provide the class ons-js-exclusive-group-item to the inputs.

Examples

Checkboxes

<fieldset class="ons-fieldset">
  <legend class="ons-fieldset__legend">
    <h1 id="fieldset-legend-title" class="ons-fieldset__legend-title ">What type of central heating do you have?</h1>
  </legend>
  <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
    <p class="ons-checkboxes__label">Select all that apply</p>
    <div class="ons-checkboxes__items">
      <p class="ons-checkboxes__item">
        <span class="ons-checkbox ">
          <input type="checkbox" id="gas" class="ons-checkbox__input ons-js-checkbox  ons-js-exclusive-group-item" value="gas">
          <label class="ons-checkbox__label  " for="gas" id="gas-label">Gas
          </label>
        </span>
      </p>
      <br>
      <p class="ons-checkboxes__item">
        <span class="ons-checkbox ">
          <input type="checkbox" id="electric" class="ons-checkbox__input ons-js-checkbox  ons-js-exclusive-group-item" value="electric">
          <label class="ons-checkbox__label  " for="electric" id="electric-label">Electric
          </label>
        </span>
      </p>
      <br>
      <p class="ons-checkboxes__item">
        <span class="ons-checkbox ">
          <input type="checkbox" id="solid-fuel" class="ons-checkbox__input ons-js-checkbox  ons-js-exclusive-group-item" value="solid-fuel">
          <label class="ons-checkbox__label  " for="solid-fuel" id="solid-fuel-label">Solid fuel
          </label>
        </span>
      </p>
      <br>
      <p class="ons-checkboxes__item">
        <span class="ons-checkbox ">
          <input type="checkbox" id="other-fuel" class="ons-checkbox__input ons-js-checkbox  ons-js-exclusive-group-item" value="other" aria-controls="other-fuel-other-wrap" aria-haspopup="true">
          <label class="ons-checkbox__label  " for="other-fuel" id="other-fuel-label">Other
          </label>
          <span class="ons-checkbox__other" id="other-fuel-other-wrap">
            <label class="ons-label ons-u-fs-s--b " for="other-fuel-textbox" id="other-fuel-textbox-label">Please specify
            </label>
            <input type="text" id="other-fuel-textbox" class="ons-input ons-input--text ons-input-type__input  ons-js-exclusive-group-item ons-input--w-auto" name="other-fuel-answer" />
          </span>
        </span>
      </p>
    </div>
    <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
      <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">Or</span>
      <span class="ons-checkbox ">
        <input type="checkbox" id="no-central-heating" class="ons-checkbox__input ons-js-checkbox ons-js-exclusive-checkbox" value="no-central-heating" data-attribute="Example attribute" data-deselect-message="Selecting this will uncheck all other checkboxes">
        <label class="ons-checkbox__label  " for="no-central-heating" id="no-central-heating-label"><span class="ons-u-vh">Or, </span> No central heating
        </label>
      </span>
    </p>
    <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="deselected" data-checkbox-adjective="deselected"></span>
  </div>
</fieldset>
{% from "components/checkboxes/_macro.njk" import onsCheckboxes %}
{{
    onsCheckboxes({
        "legend": "What type of central heating do you have?",
        "legendIsQuestionTitle": true,
        "checkboxesLabel": "Select all that apply",
        "name": "mutually-exclusive",
        "checkboxes": [
            {
                "id": "gas",
                "label": {
                    "text": "Gas"
                },
                "value": "gas"
            },
            {
                "id": "electric",
                "label": {
                    "text": "Electric"
                },
                "value": "electric"
            },
            {
                "id": "solid-fuel",
                "label": {
                    "text": "Solid fuel"
                },
                "value": "solid-fuel"
            },
            {
                "id": "other-fuel",
                "label": {
                    "text": "Other"
                },
                "value": "other",
                "other": {
                    "id": "other-fuel-textbox",
                    "name": "other-fuel-answer",
                    "label": {
                       "text": "Please specify"
                    }
                }
            }
        ],
        "mutuallyExclusive": {
            "or": "Or",
            "deselectMessage": "Selecting this will uncheck all other checkboxes",
            "deselectGroupAdjective": "deselected",
            "deselectCheckboxAdjective": "deselected",
            "checkbox": {
                "id": "no-central-heating",
                "label": {
                    "text": "No central heating"
                },
                "attributes": {
                    "data-attribute": "Example attribute"
                },
                "value": "no-central-heating"
            }
        }
    })
}}
Name Type Required Description
checkbox MutuallyExclusiveCheckbox true Configuration for the mutually exclusive checkbox
or string true Text for the “Or” label
deselectMessage string true The text the aria-live will read to warn that selecting the exclusive option will clear all other inputs
deselectGroupAdjective string true The text the aria-live will read when a field is deselected
deselectCheckboxAdjective string true The text the aria-live will read when the checkbox is deselected
error Error (ref) false Configuration for validation errors

{% macro onsMutuallyExclusive(params) %}
    {% from "components/fieldset/_macro.njk" import onsFieldset %}
    {% from "components/checkboxes/_checkbox-macro.njk" import onsCheckbox %}

    {# Resolves caller issue in jijna: https://github.com/pallets/jinja/issues/371 #}
    {% set content = caller() %}

    {% call onsFieldset({
        "id": params.id,
        "classes": params.classes,
        "legend": params.legend,
        "legendClasses": params.legendClasses,
        "description": params.description,
        "attributes": params.attributes,
        "dontWrap": params.dontWrap,
        "legendIsQuestionTitle": params.legendIsQuestionTitle,
        "error": params.error
    }) %}
        <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
            {{ content | safe }}
            {% if params.autosuggestResults is defined and params.autosuggestResults %}
                {{ params.autosuggestResults | safe }}
            {% endif %}
            <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
                <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">{{ params.or }}</span>
                {{
                    onsCheckbox({
                        "id": params.checkbox.id,
                        "name": params.checkbox.name,
                        "value": params.checkbox.value,
                        "checked": params.checkbox.checked,
                        "classes": params.checkbox.classes,
                        "attributes": params.checkbox.attributes,
                        "deselectMessage": params.deselectMessage,
                        "inputClasses": "ons-js-exclusive-checkbox",
                        "label": {
                            "text": '<span class="ons-u-vh">' + params.or + ', </span> ' + params.checkbox.label.text,
                            "description": params.checkbox.label.description
                        }
                    })
                }}
            </p>
            <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="{{ params.deselectGroupAdjective }}" data-checkbox-adjective="{{ params.deselectCheckboxAdjective }}"></span>
        </div>
    {% endcall %}
{% endmacro %}

Date

<div class="ons-question">
  <fieldset class="ons-fieldset">
    <legend class="ons-fieldset__legend">
      <h1 id="fieldset-legend-title" class="ons-fieldset__legend-title ">When did you leave your last paid job?</h1>
      <span class="ons-fieldset__description ons-fieldset__description--title">
        <p>For example, 31 3 2018</p>
      </span>
    </legend>
    <div class="ons-input-items">
      <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
        <div class="ons-field-group">
          <div class="ons-field">
            <label class="ons-label  " for="date-mutually-exclusive-day">Day
            </label>
            <input type="text" id="date-mutually-exclusive-day" class="ons-input ons-input--text ons-input-type__input  ons-js-exclusive-group-item ons-input-number--w-2" name="day-exclusive" min="1" max="31" maxlength="2" pattern="[0-9]*" inputmode="numeric" />
          </div>
          <div class="ons-field">
            <label class="ons-label  " for="date-mutually-exclusive-month">Month
            </label>
            <input type="text" id="date-mutually-exclusive-month" class="ons-input ons-input--text ons-input-type__input  ons-js-exclusive-group-item ons-input-number--w-2" name="month-exclusive" min="1" max="12" maxlength="2" pattern="[0-9]*" inputmode="numeric" />
          </div>
          <div class="ons-field">
            <label class="ons-label  " for="date-mutually-exclusive-year">Year
            </label>
            <input type="text" id="date-mutually-exclusive-year" class="ons-input ons-input--text ons-input-type__input  ons-js-exclusive-group-item ons-input-number--w-4" name="year-exclusive" min="1000" max="3000" maxlength="4" pattern="[0-9]*" inputmode="numeric" />
          </div>
        </div>
        <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
          <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">Or</span>
          <span class="ons-checkbox ">
            <input type="checkbox" id="date-exclusive-checkbox" class="ons-checkbox__input ons-js-checkbox ons-js-exclusive-checkbox" value="no-paid-job" name="no-paid-job" data-deselect-message="Selecting this will clear the date if one has been inputted">
            <label class="ons-checkbox__label  " for="date-exclusive-checkbox" id="date-exclusive-checkbox-label"><span class="ons-u-vh">Or, </span> I have never had a paid job
            </label>
          </span>
        </p>
        <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="cleared" data-checkbox-adjective="deselected"></span>
      </div>
    </div>
  </fieldset>
</div>
{% from "components/date-input/_macro.njk" import onsDateInput %}
{% from "components/question/_macro.njk" import onsQuestion %}

{% call onsQuestion({
    "title": "When did you leave your last paid job?",
    "description": "<p>For example, 31 3 2018</p>",
    "legendIsQuestionTitle": true
}) %}
    {{
        onsDateInput({
            "id": "date-mutually-exclusive",
            "dontWrap": true,
            "day": {
                "label": {
                    "text": "Day"
                },
                "name": "day-exclusive"
            },
            "month": {
                "label": {
                    "text": "Month"
                },
                "name": "month-exclusive"
            },
            "year": {
                "label": {
                    "text": "Year"
                },
                "name": "year-exclusive"
            },
            "mutuallyExclusive": {
                "or": "Or",
                "deselectMessage": "Selecting this will clear the date if one has been inputted",
                "deselectGroupAdjective": "cleared",
                "deselectCheckboxAdjective": "deselected",
                "checkbox": {
                    "id": "date-exclusive-checkbox",
                    "name": "no-paid-job",
                    "value": "no-paid-job",
                    "label": {
                        "text": "I have never had a paid job"
                    }
                }
            }
        })
    }}
{% endcall %}
Name Type Required Description
checkbox MutuallyExclusiveCheckbox true Configuration for the mutually exclusive checkbox
or string true Text for the “Or” label
deselectMessage string true The text the aria-live will read to warn that selecting the exclusive option will clear all other inputs
deselectGroupAdjective string true The text the aria-live will read when a field is deselected
deselectCheckboxAdjective string true The text the aria-live will read when the checkbox is deselected
error Error (ref) false Configuration for validation errors

{% macro onsMutuallyExclusive(params) %}
    {% from "components/fieldset/_macro.njk" import onsFieldset %}
    {% from "components/checkboxes/_checkbox-macro.njk" import onsCheckbox %}

    {# Resolves caller issue in jijna: https://github.com/pallets/jinja/issues/371 #}
    {% set content = caller() %}

    {% call onsFieldset({
        "id": params.id,
        "classes": params.classes,
        "legend": params.legend,
        "legendClasses": params.legendClasses,
        "description": params.description,
        "attributes": params.attributes,
        "dontWrap": params.dontWrap,
        "legendIsQuestionTitle": params.legendIsQuestionTitle,
        "error": params.error
    }) %}
        <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
            {{ content | safe }}
            {% if params.autosuggestResults is defined and params.autosuggestResults %}
                {{ params.autosuggestResults | safe }}
            {% endif %}
            <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
                <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">{{ params.or }}</span>
                {{
                    onsCheckbox({
                        "id": params.checkbox.id,
                        "name": params.checkbox.name,
                        "value": params.checkbox.value,
                        "checked": params.checkbox.checked,
                        "classes": params.checkbox.classes,
                        "attributes": params.checkbox.attributes,
                        "deselectMessage": params.deselectMessage,
                        "inputClasses": "ons-js-exclusive-checkbox",
                        "label": {
                            "text": '<span class="ons-u-vh">' + params.or + ', </span> ' + params.checkbox.label.text,
                            "description": params.checkbox.label.description
                        }
                    })
                }}
            </p>
            <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="{{ params.deselectGroupAdjective }}" data-checkbox-adjective="{{ params.deselectCheckboxAdjective }}"></span>
        </div>
    {% endcall %}
{% endmacro %}

Textarea

<div class="ons-question">
  <fieldset class="ons-fieldset">
    <legend class="ons-fieldset__legend">
      <h1 id="fieldset-legend-title" class="ons-fieldset__legend-title ">What do you think of this service?</h1>
    </legend>
    <div class="ons-input-items">
      <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
        <div class="ons-field">
          <label class="ons-label  " for="feedback">Enter your feedback
          </label>
          <textarea id="feedback" class="ons-input ons-input--textarea  ons-js-char-limit-input ons-js-exclusive-group-item  ons-input--w-30" name="feedback" rows="8" maxlength="200" data-char-limit-ref="feedback-lim-remaining" aria-describedby="feedback-lim-remaining"></textarea>
          <span id="feedback-lim-remaining" class="ons-input__limit ons-u-fs-s--b ons-u-d-no ons-u-mt-xs" data-charcount-singular="You have {x} character remaining" data-charcount-plural="You have {x} characters remaining" data-charcount-limit-singular="" data-charcount-limit-plural="">
          </span>
        </div>
        <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
          <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">Or</span>
          <span class="ons-checkbox ">
            <input type="checkbox" id="feedback-checkbox" class="ons-checkbox__input ons-js-checkbox ons-js-exclusive-checkbox" value="no-feedback" name="no-feedback" data-deselect-message="Selecting this will clear your feedback">
            <label class="ons-checkbox__label  " for="feedback-checkbox" id="feedback-checkbox-label"><span class="ons-u-vh">Or, </span> I don’t want to provide feedback
            </label>
          </span>
        </p>
        <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="cleared" data-checkbox-adjective="deselected"></span>
      </div>
    </div>
  </fieldset>
</div>
{% from "components/textarea/_macro.njk" import onsTextarea %}
{% from "components/question/_macro.njk" import onsQuestion %}

{% call onsQuestion({
    "title": "What do you think of this service?",
    "legendIsQuestionTitle": true
}) %}
    {{
        onsTextarea({
            "id": "feedback",
            "name": "feedback",
            "dontWrap": true,
            "width": "30",
            "label": {
                "text": "Enter your feedback"
            },
            "charCheckLimit": {
                "limit": 200,
                "charCountSingular": "You have {x} character remaining",
                "charCountPlural": "You have {x} characters remaining"
            },
            "mutuallyExclusive": {
                "or": "Or",
                "deselectMessage": "Selecting this will clear your feedback",
                "deselectGroupAdjective": "cleared",
                "deselectCheckboxAdjective": "deselected",
                "checkbox": {
                    "id": "feedback-checkbox",
                    "name": "no-feedback",
                    "value": "no-feedback",
                    "label": {
                        "text": "I don’t want to provide feedback"
                    }
                }
            }
        })
    }}
{% endcall %}
Name Type Required Description
checkbox MutuallyExclusiveCheckbox true Configuration for the mutually exclusive checkbox
or string true Text for the “Or” label
deselectMessage string true The text the aria-live will read to warn that selecting the exclusive option will clear all other inputs
deselectGroupAdjective string true The text the aria-live will read when a field is deselected
deselectCheckboxAdjective string true The text the aria-live will read when the checkbox is deselected
error Error (ref) false Configuration for validation errors

{% macro onsMutuallyExclusive(params) %}
    {% from "components/fieldset/_macro.njk" import onsFieldset %}
    {% from "components/checkboxes/_checkbox-macro.njk" import onsCheckbox %}

    {# Resolves caller issue in jijna: https://github.com/pallets/jinja/issues/371 #}
    {% set content = caller() %}

    {% call onsFieldset({
        "id": params.id,
        "classes": params.classes,
        "legend": params.legend,
        "legendClasses": params.legendClasses,
        "description": params.description,
        "attributes": params.attributes,
        "dontWrap": params.dontWrap,
        "legendIsQuestionTitle": params.legendIsQuestionTitle,
        "error": params.error
    }) %}
        <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
            {{ content | safe }}
            {% if params.autosuggestResults is defined and params.autosuggestResults %}
                {{ params.autosuggestResults | safe }}
            {% endif %}
            <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
                <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">{{ params.or }}</span>
                {{
                    onsCheckbox({
                        "id": params.checkbox.id,
                        "name": params.checkbox.name,
                        "value": params.checkbox.value,
                        "checked": params.checkbox.checked,
                        "classes": params.checkbox.classes,
                        "attributes": params.checkbox.attributes,
                        "deselectMessage": params.deselectMessage,
                        "inputClasses": "ons-js-exclusive-checkbox",
                        "label": {
                            "text": '<span class="ons-u-vh">' + params.or + ', </span> ' + params.checkbox.label.text,
                            "description": params.checkbox.label.description
                        }
                    })
                }}
            </p>
            <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="{{ params.deselectGroupAdjective }}" data-checkbox-adjective="{{ params.deselectCheckboxAdjective }}"></span>
        </div>
    {% endcall %}
{% endmacro %}

Email

<div class="ons-question">
  <h1 id="question-title" class="ons-question__title">Get a confirmation email</h1>
  <span class="ons-question__description">
    <p>Enter your email address if you would like to be sent confirmation that you have completed your survey</p>
  </span>
  <fieldset class="ons-fieldset">
    <legend class="ons-fieldset__legend undefinedons-js-input-legend">
    </legend>
    <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
      <label class="ons-label  ons-label--with-description " for="email">Email address
      </label>
      <span id="description-hint" class="ons-label__description  ons-input--with-description">
        This will not be stored and only used once to send your confirmation
      </span>
      <input type="email" id="email" class="ons-input ons-input--text ons-input-type__input ons-js-exclusive-group-item" aria-describedby="description-hint" />
      <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
        <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">Or</span>
        <span class="ons-checkbox ">
          <input type="checkbox" id="email-checkbox" class="ons-checkbox__input ons-js-checkbox ons-js-exclusive-checkbox" value="no-email" name="no-email" data-deselect-message="Selecting this will clear your email">
          <label class="ons-checkbox__label  " for="email-checkbox" id="email-checkbox-label"><span class="ons-u-vh">Or, </span> I don’t want to receive a confirmation email
          </label>
        </span>
      </p>
      <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="cleared" data-checkbox-adjective="deselected"></span>
    </div>
  </fieldset>
</div>
{% from "components/input/_macro.njk" import onsInput %}
{% from "components/question/_macro.njk" import onsQuestion %}

{% call onsQuestion({
    "title": "Get a confirmation email",
    "description": "<p>Enter your email address if you would like to be sent confirmation that you have completed your survey</p>"
}) %}
    {{
        onsInput({
            "id": "email",
            "type": "email",
            "label": {
                "text": "Email address",
                "description": "This will not be stored and only used once to send your confirmation"
            },
            "mutuallyExclusive": {
                "or": "Or",
                "deselectMessage": "Selecting this will clear your email",
                "deselectGroupAdjective": "cleared",
                "deselectCheckboxAdjective": "deselected",
                "checkbox": {
                    "id": "email-checkbox",
                    "name": "no-email",
                    "value": "no-email",
                    "label": {
                        "text": "I don’t want to receive a confirmation email"
                    }
                }
            }
        })
    }}
{% endcall %}
Name Type Required Description
checkbox MutuallyExclusiveCheckbox true Configuration for the mutually exclusive checkbox
or string true Text for the “Or” label
deselectMessage string true The text the aria-live will read to warn that selecting the exclusive option will clear all other inputs
deselectGroupAdjective string true The text the aria-live will read when a field is deselected
deselectCheckboxAdjective string true The text the aria-live will read when the checkbox is deselected
error Error (ref) false Configuration for validation errors

{% macro onsMutuallyExclusive(params) %}
    {% from "components/fieldset/_macro.njk" import onsFieldset %}
    {% from "components/checkboxes/_checkbox-macro.njk" import onsCheckbox %}

    {# Resolves caller issue in jijna: https://github.com/pallets/jinja/issues/371 #}
    {% set content = caller() %}

    {% call onsFieldset({
        "id": params.id,
        "classes": params.classes,
        "legend": params.legend,
        "legendClasses": params.legendClasses,
        "description": params.description,
        "attributes": params.attributes,
        "dontWrap": params.dontWrap,
        "legendIsQuestionTitle": params.legendIsQuestionTitle,
        "error": params.error
    }) %}
        <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
            {{ content | safe }}
            {% if params.autosuggestResults is defined and params.autosuggestResults %}
                {{ params.autosuggestResults | safe }}
            {% endif %}
            <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
                <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">{{ params.or }}</span>
                {{
                    onsCheckbox({
                        "id": params.checkbox.id,
                        "name": params.checkbox.name,
                        "value": params.checkbox.value,
                        "checked": params.checkbox.checked,
                        "classes": params.checkbox.classes,
                        "attributes": params.checkbox.attributes,
                        "deselectMessage": params.deselectMessage,
                        "inputClasses": "ons-js-exclusive-checkbox",
                        "label": {
                            "text": '<span class="ons-u-vh">' + params.or + ', </span> ' + params.checkbox.label.text,
                            "description": params.checkbox.label.description
                        }
                    })
                }}
            </p>
            <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="{{ params.deselectGroupAdjective }}" data-checkbox-adjective="{{ params.deselectCheckboxAdjective }}"></span>
        </div>
    {% endcall %}
{% endmacro %}

Number/Currency

<div class="ons-question">
  <h1 id="question-title" class="ons-question__title">What was your annual income before tax in 2018/19?</h1>
  <fieldset class="ons-fieldset">
    <legend class="ons-fieldset__legend undefinedons-js-input-legend">
    </legend>
    <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
      <label class="ons-label  " for="currency">Gross annual income
      </label>
      <span class="ons-input-type ons-input-type--prefix">
        <span class="ons-input-type__inner">
          <input type="text" id="currency" class="ons-input ons-input--text ons-input-type__input ons-input-number--w-4 ons-js-exclusive-group-item" title="British pounds (GBP)" pattern="[0-9]*" inputmode="numeric" min />
          <abbr class="ons-input-type__type ons-js-input-abbr" aria-hidden="true" title="British pounds (GBP)">£</abbr>
        </span>
      </span>
      <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
        <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">Or</span>
        <span class="ons-checkbox ">
          <input type="checkbox" id="currency-checkbox" class="ons-checkbox__input ons-js-checkbox ons-js-exclusive-checkbox" value="no-currency" name="no-currency" data-deselect-message="Selecting this will clear your inputted annual income">
          <label class="ons-checkbox__label  " for="currency-checkbox" id="currency-checkbox-label"><span class="ons-u-vh">Or, </span> I prefer not to say
          </label>
        </span>
      </p>
      <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="cleared" data-checkbox-adjective="deselected"></span>
    </div>
  </fieldset>
</div>
{% from "components/input/_macro.njk" import onsInput %}
{% from "components/question/_macro.njk" import onsQuestion %}

{% call onsQuestion({
    "title": "What was your annual income before tax in 2018/19?"
}) %}
    {{
        onsInput({
            "id": "currency",
            "type": "number",
            "width": "4",
            "attributes": {
                "min": 0
            },
            "label": {
                "text": "Gross annual income"
            },
            "prefix": {
                "title": "British pounds (GBP)",
                "text": "£"
            },
            "mutuallyExclusive": {
                "or": "Or",
                "deselectMessage": "Selecting this will clear your inputted annual income",
                "deselectGroupAdjective": "cleared",
                "deselectCheckboxAdjective": "deselected",
                "checkbox": {
                    "id": "currency-checkbox",
                    "name": "no-currency",
                    "value": "no-currency",
                    "label": {
                        "text": "I prefer not to say"
                    }
                }
            }
        })
    }}
{% endcall %}
Name Type Required Description
checkbox MutuallyExclusiveCheckbox true Configuration for the mutually exclusive checkbox
or string true Text for the “Or” label
deselectMessage string true The text the aria-live will read to warn that selecting the exclusive option will clear all other inputs
deselectGroupAdjective string true The text the aria-live will read when a field is deselected
deselectCheckboxAdjective string true The text the aria-live will read when the checkbox is deselected
error Error (ref) false Configuration for validation errors

{% macro onsMutuallyExclusive(params) %}
    {% from "components/fieldset/_macro.njk" import onsFieldset %}
    {% from "components/checkboxes/_checkbox-macro.njk" import onsCheckbox %}

    {# Resolves caller issue in jijna: https://github.com/pallets/jinja/issues/371 #}
    {% set content = caller() %}

    {% call onsFieldset({
        "id": params.id,
        "classes": params.classes,
        "legend": params.legend,
        "legendClasses": params.legendClasses,
        "description": params.description,
        "attributes": params.attributes,
        "dontWrap": params.dontWrap,
        "legendIsQuestionTitle": params.legendIsQuestionTitle,
        "error": params.error
    }) %}
        <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
            {{ content | safe }}
            {% if params.autosuggestResults is defined and params.autosuggestResults %}
                {{ params.autosuggestResults | safe }}
            {% endif %}
            <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
                <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">{{ params.or }}</span>
                {{
                    onsCheckbox({
                        "id": params.checkbox.id,
                        "name": params.checkbox.name,
                        "value": params.checkbox.value,
                        "checked": params.checkbox.checked,
                        "classes": params.checkbox.classes,
                        "attributes": params.checkbox.attributes,
                        "deselectMessage": params.deselectMessage,
                        "inputClasses": "ons-js-exclusive-checkbox",
                        "label": {
                            "text": '<span class="ons-u-vh">' + params.or + ', </span> ' + params.checkbox.label.text,
                            "description": params.checkbox.label.description
                        }
                    })
                }}
            </p>
            <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="{{ params.deselectGroupAdjective }}" data-checkbox-adjective="{{ params.deselectCheckboxAdjective }}"></span>
        </div>
    {% endcall %}
{% endmacro %}

Duration

<div class="ons-question">
  <fieldset class="ons-fieldset">
    <legend class="ons-fieldset__legend">
      <h1 id="fieldset-legend-title" class="ons-fieldset__legend-title ">How long have you lived at this address?</h1>
      <span class="ons-fieldset__description ons-fieldset__description--title">
        <p>Enter “0” into the years field if you have lived at this address for less than a year</p>
      </span>
    </legend>
    <div class="ons-input-items">
      <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
        <div class="ons-field-group">
          <div class="ons-field">
            <span class="ons-input-type">
              <span class="ons-input-type__inner">
                <input type="text" id="address-duration-years" class="ons-input ons-input--text ons-input-type__input  ons-js-exclusive-group-item ons-input-number--w-2" title="Years" name="address-duration-years" pattern="[0-9]*" inputmode="numeric" />
                <abbr class="ons-input-type__type ons-js-input-abbr" aria-hidden="true" title="Years">Years</abbr>
              </span>
            </span>
          </div>
          <div class="ons-field">
            <span class="ons-input-type">
              <span class="ons-input-type__inner">
                <input type="text" id="address-duration-months" class="ons-input ons-input--text ons-input-type__input  ons-js-exclusive-group-item ons-input-number--w-2" title="Months" name="address-duration-months" pattern="[0-9]*" inputmode="numeric" />
                <abbr class="ons-input-type__type ons-js-input-abbr" aria-hidden="true" title="Months">Months</abbr>
              </span>
            </span>
          </div>
        </div>
        <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
          <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">Or</span>
          <span class="ons-checkbox ">
            <input type="checkbox" id="duration-exclusive-checkbox" class="ons-checkbox__input ons-js-checkbox ons-js-exclusive-checkbox" value="no-duration" name="no-duration" data-deselect-message="Selecting this will clear the date if one has been inputted">
            <label class="ons-checkbox__label  " for="duration-exclusive-checkbox" id="duration-exclusive-checkbox-label"><span class="ons-u-vh">Or, </span> I have not moved in to this address yet
            </label>
          </span>
        </p>
        <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="cleared" data-checkbox-adjective="deselected"></span>
      </div>
    </div>
  </fieldset>
</div>
{% from "components/question/_macro.njk" import onsQuestion %}
{% from "components/duration/_macro.njk" import onsDuration %}

{% call onsQuestion({
    "title": "How long have you lived at this address?",
    "description": "<p>Enter “0” into the years field if you have lived at this address for less than a year</p>",
    "legendIsQuestionTitle": true
}) %}
    {{ onsDuration({
        "id": "address-duration",
        "dontWrap": true,
        "field1": {
            "id": "address-duration-years",
            "name": "address-duration-years",
            "suffix": "Years"
        },
        "field2": {
            "id": "address-duration-months",
            "name": "address-duration-months",
            "suffix": "Months"
        },
        "mutuallyExclusive": {
            "or": "Or",
            "deselectMessage": "Selecting this will clear the date if one has been inputted",
            "deselectGroupAdjective": "cleared",
            "deselectCheckboxAdjective": "deselected",
            "checkbox": {
                "id": "duration-exclusive-checkbox",
                "name": "no-duration",
                "value": "no-duration",
                "label": {
                    "text": "I have not moved in to this address yet"
                }
            }
        }
    }) }}
{% endcall %}
Name Type Required Description
checkbox MutuallyExclusiveCheckbox true Configuration for the mutually exclusive checkbox
or string true Text for the “Or” label
deselectMessage string true The text the aria-live will read to warn that selecting the exclusive option will clear all other inputs
deselectGroupAdjective string true The text the aria-live will read when a field is deselected
deselectCheckboxAdjective string true The text the aria-live will read when the checkbox is deselected
error Error (ref) false Configuration for validation errors

{% macro onsMutuallyExclusive(params) %}
    {% from "components/fieldset/_macro.njk" import onsFieldset %}
    {% from "components/checkboxes/_checkbox-macro.njk" import onsCheckbox %}

    {# Resolves caller issue in jijna: https://github.com/pallets/jinja/issues/371 #}
    {% set content = caller() %}

    {% call onsFieldset({
        "id": params.id,
        "classes": params.classes,
        "legend": params.legend,
        "legendClasses": params.legendClasses,
        "description": params.description,
        "attributes": params.attributes,
        "dontWrap": params.dontWrap,
        "legendIsQuestionTitle": params.legendIsQuestionTitle,
        "error": params.error
    }) %}
        <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
            {{ content | safe }}
            {% if params.autosuggestResults is defined and params.autosuggestResults %}
                {{ params.autosuggestResults | safe }}
            {% endif %}
            <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
                <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">{{ params.or }}</span>
                {{
                    onsCheckbox({
                        "id": params.checkbox.id,
                        "name": params.checkbox.name,
                        "value": params.checkbox.value,
                        "checked": params.checkbox.checked,
                        "classes": params.checkbox.classes,
                        "attributes": params.checkbox.attributes,
                        "deselectMessage": params.deselectMessage,
                        "inputClasses": "ons-js-exclusive-checkbox",
                        "label": {
                            "text": '<span class="ons-u-vh">' + params.or + ', </span> ' + params.checkbox.label.text,
                            "description": params.checkbox.label.description
                        }
                    })
                }}
            </p>
            <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="{{ params.deselectGroupAdjective }}" data-checkbox-adjective="{{ params.deselectCheckboxAdjective }}"></span>
        </div>
    {% endcall %}
{% endmacro %}

How to check mutually exclusive answers

To help users select an answer, you should:

  • check they have selected at least one checkbox
  • show an error message if they have not selected any checkbox, including the mutually exclusive checkbox
  • show an error message if they have not entered something in any of the fields or selected the mutually exclusive checkbox
  • show an error message if what has been entered in an input field is not valid

Error messages

Use the correct errors pattern and show the error details above the fieldset.

<div class="ons-panel ons-panel--error ons-panel--no-title ons-u-mb-s" id="date-mutually-exclusive-error">
  <span class="ons-u-vh">Error: </span>
  <div class="ons-panel__body">
    <p class="ons-panel__error">
      <strong>Enter the date or select an answer</strong>
    </p>
    <fieldset id="date-mutually-exclusive" class="ons-fieldset">
      <legend class="ons-fieldset__legend">
      </legend>
      <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
        <div class="ons-field-group">
          <div class="ons-field">
            <label class="ons-label  " for="date-mutually-exclusive-day">Day
            </label>
            <input type="text" id="date-mutually-exclusive-day" class="ons-input ons-input--text ons-input-type__input  ons-input--error ons-js-exclusive-group-item ons-input-number--w-2" name="day-exclusive" min="1" max="31" maxlength="2" pattern="[0-9]*" inputmode="numeric" />
          </div>
          <div class="ons-field">
            <label class="ons-label  " for="date-mutually-exclusive-month">Month
            </label>
            <input type="text" id="date-mutually-exclusive-month" class="ons-input ons-input--text ons-input-type__input  ons-input--error ons-js-exclusive-group-item ons-input-number--w-2" name="month-exclusive" min="1" max="12" maxlength="2" pattern="[0-9]*" inputmode="numeric" />
          </div>
          <div class="ons-field">
            <label class="ons-label  " for="date-mutually-exclusive-year">Year
            </label>
            <input type="text" id="date-mutually-exclusive-year" class="ons-input ons-input--text ons-input-type__input  ons-input--error ons-js-exclusive-group-item ons-input-number--w-4" name="year-exclusive" min="1000" max="3000" maxlength="4" pattern="[0-9]*" inputmode="numeric" />
          </div>
        </div>
        <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
          <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">Or</span>
          <span class="ons-checkbox ">
            <input type="checkbox" id="date-exclusive-checkbox" class="ons-checkbox__input ons-js-checkbox ons-js-exclusive-checkbox" value="no-paid-job" name="no-paid-job" data-deselect-message="Selecting this will clear the date if one has been inputted">
            <label class="ons-checkbox__label  " for="date-exclusive-checkbox" id="date-exclusive-checkbox-label"><span class="ons-u-vh">Or, </span> I have never had a paid job
            </label>
          </span>
        </p>
        <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="cleared" data-checkbox-adjective="deselected"></span>
      </div>
    </fieldset>
  </div>
</div>
{% from "components/date-input/_macro.njk" import onsDateInput %}

{{
    onsDateInput({
        "id": "date-mutually-exclusive",
        "day": {
            "label": {
                "text": "Day"
            },
            "name": "day-exclusive"
        },
        "month": {
            "label": {
                "text": "Month"
            },
            "name": "month-exclusive"
        },
        "year": {
            "label": {
                "text": "Year"
            },
            "name": "year-exclusive"
        },
        "mutuallyExclusive": {
            "or": "Or",
            "deselectMessage": "Selecting this will clear the date if one has been inputted",
            "deselectGroupAdjective": "cleared",
            "deselectCheckboxAdjective": "deselected",
            "checkbox": {
                "id": "date-exclusive-checkbox",
                "name": "no-paid-job",
                "value": "no-paid-job",
                "label": {
                    "text": "I have never had a paid job"
                }
            }
        },
        "error": {
            "id": 'date-mutually-exclusive-error',
            "text": 'Enter the date or select an answer',
            "dsExample": isPatternLib
        }
    })
}}
Name Type Required Description
checkbox MutuallyExclusiveCheckbox true Configuration for the mutually exclusive checkbox
or string true Text for the “Or” label
deselectMessage string true The text the aria-live will read to warn that selecting the exclusive option will clear all other inputs
deselectGroupAdjective string true The text the aria-live will read when a field is deselected
deselectCheckboxAdjective string true The text the aria-live will read when the checkbox is deselected
error Error (ref) false Configuration for validation errors

{% macro onsMutuallyExclusive(params) %}
    {% from "components/fieldset/_macro.njk" import onsFieldset %}
    {% from "components/checkboxes/_checkbox-macro.njk" import onsCheckbox %}

    {# Resolves caller issue in jijna: https://github.com/pallets/jinja/issues/371 #}
    {% set content = caller() %}

    {% call onsFieldset({
        "id": params.id,
        "classes": params.classes,
        "legend": params.legend,
        "legendClasses": params.legendClasses,
        "description": params.description,
        "attributes": params.attributes,
        "dontWrap": params.dontWrap,
        "legendIsQuestionTitle": params.legendIsQuestionTitle,
        "error": params.error
    }) %}
        <div class="ons-js-mutually-exclusive ons-mutually-exclusive">
            {{ content | safe }}
            {% if params.autosuggestResults is defined and params.autosuggestResults %}
                {{ params.autosuggestResults | safe }}
            {% endif %}
            <p class="ons-checkboxes--mutually-exclusive__item ons-u-mt-s">
                <span class="ons-checkboxes__label ons-u-fs-r--b" aria-hidden="true">{{ params.or }}</span>
                {{
                    onsCheckbox({
                        "id": params.checkbox.id,
                        "name": params.checkbox.name,
                        "value": params.checkbox.value,
                        "checked": params.checkbox.checked,
                        "classes": params.checkbox.classes,
                        "attributes": params.checkbox.attributes,
                        "deselectMessage": params.deselectMessage,
                        "inputClasses": "ons-js-exclusive-checkbox",
                        "label": {
                            "text": '<span class="ons-u-vh">' + params.or + ', </span> ' + params.checkbox.label.text,
                            "description": params.checkbox.label.description
                        }
                    })
                }}
            </p>
            <span class="ons-js-exclusive-alert ons-u-vh" role="alert" aria-live="polite" data-group-adjective="{{ params.deselectGroupAdjective }}" data-checkbox-adjective="{{ params.deselectCheckboxAdjective }}"></span>
        </div>
    {% endcall %}
{% endmacro %}

Error validation for this component varies according to the mutually exclusive answer type used.

If no option is selected for a checkbox-only answer

Use “Select [whatever it is]”.
For example, “Select what type of central heating you have”.

If all fields are empty and the mutually exclusive checkbox is not selected

Use “Enter [whatever the field asks for] or select the other option”.
For example, “Enter your feedback or select the other option”.

Each pattern and component that uses the mutually exclusive component has guidance on writing error messages:

Help improve this component

Let us know how we could improve this component or share your user research findings. Discuss the ‘Mutually Exclusive’ component on GitHub