Skip to main content

Testing

Help us to improve the ONS Design System. Take part in a short study

Button

<button type="submit" class="btn">
  <span class="btn__inner">Save and continue</span>
</button>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
{{
    onsButton({
        "text": "Save and continue"
    })
}}

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

When to use this component

Buttons are used to direct the user to perform a specific interaction. The copy used on the button should be clear, concise and direct.

How it works

Write button text in sentence case, describing the action it performs. For example ‘Save and continue’ or ‘Start now’.

Align the primary action button to the left edge of your form.

There are 2 ways to use the button component. You can use HTML or, if you are using Nunjucks, you can use the Nunjucks macro.

Variations

Small

In certain circumstances there may be the need for multiple primary actions to exist on a page. The small primary button should be used in this scenario only when each action has the same context, for example, a list containing business surveys that a user needs to complete.

<button type="button" class="btn btn--small">
  <span class="btn__inner">Add</span>
</button>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
{{
    onsButton({
        "type": 'button',
        "text": 'Add',
        "classes": 'btn--small'
    })
}}

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Secondary

Secondary buttons should be used for supplementary actions which users can take. Secondary buttons cannot exist without a primary button in the same context.

<button type="button" class="btn btn--secondary">
  <span class="btn__inner">Add a person</span>
</button>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
{{
    onsButton({
        "type": 'button',
        "text": 'Add a person',
        "classes": 'btn--secondary'
    })
}}

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Secondary (small)

A small secondary button should be used within the same context of a primary small button when there is an additional action available along with the primary action.

<button type="button" class="btn btn--secondary btn--small">
  <span class="btn__inner">Add</span>
</button>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
{{
    onsButton({
        "type": 'button',
        "text": 'Add',
        "classes": 'btn--secondary btn--small'
    })
}}

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Call to action

The call to action button should be used when a link is the primary call to action on a page. Links styled as buttons are not accessible without certain attributes. We add role=button to the html which allows users that use voice commands to select the button.

<a href="#" role="button" class="btn u-mb-m btn--link js-submit-btn">
  <span class="btn__inner">Get started
    <svg class="svg-icon" viewBox="0 0 8 13" xmlns="http://www.w3.org/2000/svg" focusable="false">
      <path d="M5.74,14.28l-.57-.56a.5.5,0,0,1,0-.71h0l5-5-5-5a.5.5,0,0,1,0-.71h0l.57-.56a.5.5,0,0,1,.71,0h0l5.93,5.93a.5.5,0,0,1,0,.7L6.45,14.28a.5.5,0,0,1-.71,0Z" transform="translate(-5.02 -1.59)" />
    </svg>
  </span>
</a>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
{{
    onsButton({
        "type": 'button',
        "text": 'Get started',
        "classes": 'u-mb-m',
        "url": '#'
    })
}}

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Call to action opening in a new window

If a call to action button needs to open in a new window, e.g. for a web chat service, the newWindow parameter should be set to true. This will then provide a description to screen reader users to inform them the button will open a new window before they click it, as it would disable the back button in the browser.

<a href="#" role="button" class="btn u-mb-m btn--link js-submit-btn" target="_blank" rel="noopener">
  <span class="btn__inner">Web chat
    <svg class="svg-icon" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" focusable="false">
      <path d="M13.5,9H13a.5.5,0,0,0-.5.5v3h-9v-9h3A.5.5,0,0,0,7,3V2.5A.5.5,0,0,0,6.5,2h-4a.5.5,0,0,0-.5.5v11a.5.5,0,0,0,.5.5h11a.5.5,0,0,0,.5-.5v-4A.5.5,0,0,0,13.5,9Z" transform="translate(-2 -1.99)" />
      <path d="M8.83,7.88a.51.51,0,0,0,.71,0l2.31-2.32,1.28,1.28A.51.51,0,0,0,14,6.49v-4a.52.52,0,0,0-.5-.5h-4A.51.51,0,0,0,9,2.52a.58.58,0,0,0,.14.33l1.28,1.28L8.12,6.46a.51.51,0,0,0,0,.71Z" transform="translate(-2 -1.99)" />
    </svg>
  </span>
  <span class="btn__new-window-description u-vh">opens in a new window</span>
</a>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
{{
    onsButton({
        "type": 'button',
        "text": 'Web chat',
        "classes": 'u-mb-m',
        "url": '#',
        "newWindow": true
    })
}}

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Group

Groups are used to group two or more buttons together and easily align them alongside eachother. This will allow for a page to have two actions available to the user such as Continue and Cancel.

<div class="btn-group">
  <button type="button" class="btn btn-group__btn">
    <span class="btn__inner">Continue</span>
  </button>
  <button type="button" class="btn btn--secondary btn-group__btn">
    <span class="btn__inner">Cancel</span>
  </button>
</div>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
<div class="btn-group">
    {{
        onsButton({
            "type": 'button',
            "text": 'Continue',
            "classes": 'btn-group__btn'
        })
    }}
    {{
        onsButton({
            "type": 'button',
            "text": 'Cancel',
            "classes": 'btn--secondary btn-group__btn'
        })
    }}
</div>

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Ghost

The ghosted button style is for use in the header or footer. This can be used to allow a user to save and sign out of the current context.

Setting the buttonStyle param to exit adds the appropriate icon for a ‘sign out’ or ‘exit’ button.

<div style="padding: 1rem 1.5rem; background: #206095">
  <a href="" role="button" class="btn btn--ghost btn--exit">
    <span class="btn__inner">Save and sign out
      <svg class="svg-icon" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" focusable="false">
        <path d="M13.85,7.65l-2.5-2.5a.5.5,0,0,0-.71,0,.48.48,0,0,0-.15.36V7h-3a.5.5,0,0,0-.5.5v1a.5.5,0,0,0,.5.5h3v1.5A.49.49,0,0,0,11,11a.48.48,0,0,0,.34-.14l2.51-2.5a.49.49,0,0,0,0-.68Z" transform="translate(-2 -2)" />
        <path d="M8.5,14h-6a.5.5,0,0,1-.5-.5V2.5A.5.5,0,0,1,2.5,2h6a.5.5,0,0,1,.5.5V3a.5.5,0,0,1-.5.5h-5v9h5A.5.5,0,0,1,9,13v.5A.5.5,0,0,1,8.5,14Z" transform="translate(-2 -2)" />
      </svg>
    </span>
  </a>
</div>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
<div style="padding: 1rem 1.5rem; background: #206095">
    {{
        onsButton({
            "type": 'button',
            "text": 'Save and sign out',
            "classes": 'btn--ghost',
            "buttonStyle": "exit"
        })
    }}
</div>

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Disabled

Use btn--disabled with the disabled=true element attribute for buttons that have no event/action but need to be displayed.

<button type="button" class="btn btn--disabled" disabled="true">
  <span class="btn__inner">Submit</span>
</button>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
{{
    onsButton({
        "type": 'button',
        "text": 'Submit',
        "classes": 'btn--disabled',
        "attributes": {
            "disabled": "true"
        }
    })
}}

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Loader

The loader button is used to help prevent user’s from clicking buttons multiple times when they are unsure if the action they have carried out has worked. This can lead to them submitting the form on the page twice. Setting SubmitType to loader will disable the button when clicked and will add a loading animation. This can be used when the button executes a process that can take some time to complete.

<form onsubmit="return false;">
  <div class="btn-group">
    <button type="submit" class="btn btn-group__btn btn--loader js-loader js-submit-btn">
      <span class="btn__inner">Submit
        <svg class="svg-icon" xmlns="http://www.w3.org/2000/svg" focusable="false" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="uil-default">
          <rect x="0" y="0" width="100" height="100" fill="none" class="bk"></rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(0 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(30 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.08333333333333333s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(60 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.16666666666666666s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(90 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.25s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(120 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.3333333333333333s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(150 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.4166666666666667s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(180 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.5s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(210 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.5833333333333334s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(240 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.6666666666666666s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(270 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.75s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(300 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.8333333333333334s' repeatCount='indefinite' />
          </rect>
          <rect x='46.5' y='40' width='7' height='20' rx='5' ry='5' transform='rotate(330 50 50) translate(0 -30)'>
            <animate attributeName='opacity' from='1' to='0' dur='1s' begin='0.9166666666666666s' repeatCount='indefinite' />
          </rect>
        </svg>
      </span>
    </button>
  </div>
</form>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
<form onsubmit="return false;">
    <div class="btn-group">
        {{
            onsButton({
                "text": 'Submit',
                "classes": 'btn-group__btn',
                "submitType": "loader"
            })
        }}
    </div>
</form>

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Timer

The timer button is aimed at stopping forms being submitted twice when users double click the submit button. Setting SubmitType to timer will disable the button for one second on click.

<form onsubmit="return false;">
  <div class="btn-group">
    <button type="submit" class="btn btn-group__btn js-timer js-submit-btn">
      <span class="btn__inner">Submit</span>
    </button>
  </div>
</form>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
<form onsubmit="return false;">
    <div class="btn-group">
        {{
            onsButton({
                "text": 'Submit',
                "classes": 'btn-group__btn',
                "submitType": "timer"
            })
        }}
    </div>
</form>

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Print

The buttonStyle parameter can be set to print to render a print button. This will take care of adding iconography and classes for the print-button.js file to pick up. The button will not render until print-button.js has run, and will not be visible on printed pages.

<button type="button" class="btn btn--print u-d-no js-print-btn">
  <span class="btn__inner">Print this page
    <svg class="svg-icon" viewBox="0 0 20 16" xmlns="http://www.w3.org/2000/svg" focusable="false">
      <path d="M17 4H3C1.3 4 0 5.2 0 6.8v5.5h4V16h12v-3.7h4V6.8C20 5.2 18.7 4 17 4zm-3 10H6V9h8v5zm3-6a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-1-8H4v3h12V0z" />
    </svg>
  </span>
</button>
Nunjucks macro options
Name Type Required Description
text string true If html is set, this is not required. Text for the button. If html is provided, the text argument will be ignored.
html string true If text is set, this is not required. HTML for the button. If html is provided, the text argument will be ignored.
type string true Type of input or buttonbutton, submit or reset. Defaults to submit.
name string true Name for the button, if the button is a link the id attribute will be set instead
value string true Value for the button
url string false If provided will create a link and adds the relevant classes
id string false ID for the button
classes string false Classes to add to the button component
innerClasses string false Classes to add to the inner button element
attributes object false HTML attributes (for example data attributes) to add to the button component
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
submitType string false If set to timer the button will only be disabled for a short time to stop double clicks from double submitting. If set to loader will create a loader button that includes the loading animation
Icon object or boolean false Object that contains the settings for adding icons to buttons. Optionally provide False as the value to not show an icon for the button.
newWindow boolean false Opens the next page in a new tab. Used for links to external pages
buttonStyle string false Can be set to print, exit or mobile. This will style the button with the relevant classes and icons
buttonContext string false Can be used to add context to a button’s text label for screen readers. For example, the “Hide this” button in the collapsible component requires context to help let a screen reader user know what the button hides.
dsExample boolean false Defaulted to true if set in Design System examples, will render a <a> tag instead of <button> to stop default submit behaviour of buttons in forms - only for use in DS examples

Icon

Name Type Required Description
iconType string true Name of the icon to be used
iconPosition string true Position of the icon in relation to the button text. Either set to before or after
{% from "components/button/_macro.njk" import onsButton %}
{{
    onsButton({
        "type": 'button',
        "text": 'Print this page',
        "buttonStyle": "print"
    })
}}

{% from "components/icons/_macro.njk" import onsIcon %}
{% macro onsButton(params) %}
    {% if params.icon is defined and params.icon and params.icon != false %}
        {% set iconType = params.icon.iconType %}
        {% set iconPosition = params.icon.iconPosition %}
    {% elif params.icon is not defined %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            {% set iconType = "external-link" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "print" %}
            {% set iconType = "print" %}
            {% set iconPosition = "after" %}
        {% elif params.buttonStyle == "exit" %}
            {% set iconType = "exit" %}
            {% set iconPosition = "after" %}
        {% elif params.submitType == "loader" %}
            {% set iconType = "loader" %}
            {% set iconPosition = "after" %}
        {% elif params.url is defined and params.url or params.buttonStyle == "mobile" %}
            {% set iconType = "chevron" %}
            {% set iconPosition = "after" %}
        {% endif %}
    {% endif %}
    {% set tag = "a" if params.url or params.buttonStyle == "exit" or params.dsExample is defined and params.dsExample else "button" %}
    <{{ tag }}
        {% if params.url is defined and params.url or params.buttonStyle == "exit" %}
            href="{{ params.url }}"
            role="button"
        {% else %}
            type="{{ params.type if params.type is defined and params.type else ('button' if params.buttonStyle == "print" else 'submit') }}"
        {% endif %}
        class="btn{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.url is defined and params.url %} btn--link js-submit-btn{% endif %}{% if params.disabled is defined and params.disabled %} btn--disabled{% endif %}{% if params.buttonStyle == "print" %} btn--print u-d-no js-print-btn{% endif %}{% if params.buttonStyle == "exit" %} btn--exit{% endif %}{% if params.submitType == "loader" %} btn--loader js-loader js-submit-btn{% endif %}{% if params.submitType == "timer" %} js-timer js-submit-btn{% endif %}"
        {% if params.id is defined and params.id %}id="{{ params.id }}"{% endif %}
        {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
        {% if params.name is defined and params.name and tag != "a" %}name="{{ params.name }}"{% elif params.name is defined and params.name and tag == "a" %}id="{{ params.name }}"{% endif %}
        {% if params.disabled is defined and params.disabled %} disabled{% endif %}
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}target="_blank" rel="noopener"{% endif %}
        {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{attribute}}="{{value}}" {% endfor %}{% endif %}
        >
        <span class="btn__inner{% if params.innerClasses is defined and params.innerClasses %} {{ params.innerClasses }}{% endif %}">
            {%- if iconPosition == "before" %}
                {{
                    onsIcon({
                        "icon": iconType
                    })
                }}
            {% endif -%}
            {{- params.html | safe if params.html is defined and params.html else params.text -}}
            {%- if iconPosition == "after" %}
              {{
                onsIcon({
                    "icon": iconType
                })
              }}
            {% endif -%}
        </span>
        {% if params.url is defined and params.url and params.newWindow is defined and params.newWindow %}
            <span class="btn__new-window-description u-vh">{{ params.newWindowDescription | default("opens in a new window") }}</span>
        {% endif %}
        {% if params.buttonContext is defined and params.buttonContext %}
            <span class="btn__context u-vh">{{ params.buttonContext }}</span>
        {% endif %}
        {% if params.listeners %}
            <script{% if csp_nonce %} nonce="{{ csp_nonce() }}"{% endif %}>
                {% for listener, value in (params.listeners.items() if params.listeners is mapping and params.listeners.items else params.listeners) %}
                    document.getElementById("{{ params.id }}").addEventListener('{{ listener }}', function(){ {{ value }} });
                {% endfor %}
            </script>
        {% endif %}
    </{{ tag }}>
{% endmacro %}

$button-border-height: 3px;
.btn {
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-block;
  font-family: inherit;
  font-size: 1rem;
  font-weight: $font-weight-bold;
  line-height: 1.35;
  margin: 0;
  padding: 0;
  position: relative;
  text-align: center;
  text-decoration: none;
  text-rendering: optimizeLegibility;
  vertical-align: top;
  // Transparent border for IE11 High Contrast mode support due to 'border: 0' on buttons
  &::after {
    border: ems($button-border-height) solid transparent;
    bottom: 0;
    content: '';
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }
  .svg-icon {
    fill: $color-text-inverse;
    height: 0.8rem;
    margin: 0 0 0.1rem 0.5rem;
    vertical-align: middle;
    width: 0.8rem;
  }
  &__inner {
    background: $color-button;
    border-bottom: ems($button-border-height) solid rgba(0, 0, 0, 0.6);
    border-radius: $input-radius;
    color: $color-text-inverse;
    display: inherit;
    padding: 0.75em 1em;
    // Required for Google Tag Manager
    pointer-events: none;
  }
  // When preceded by another button (e.g. in a group)
  & + & {
    margin-left: 0.5rem;
  }
  // When focussed
  &:focus:not(:active):not(:hover) {
    outline: 3px solid transparent;
  }
  &:focus:not(:active):not(:hover) &__inner {
    background-color: $color-focus;
  }
  &:focus &__inner {
    color: $color-text-link-focus;
  }
  &:focus:hover &__inner {
    background: darken($color-focus, 5%);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus,
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):focus:hover {
    outline: none;
  }
  // When clicked
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active {
    padding-top: ems(3px);
  }
  &:not([class*='btn--ghost']):not([class*='btn--mobile']):active &__inner {
    border-bottom: 0;
  }
  // Modifiers
  &--small,
  &--mobile {
    font-size: 0.9rem;
  }
  &--small &,
  &--mobile & {
    &__inner {
      padding: 0.5em 0.7em;
    }
  }
  &--inline & {
    &__inner {
      padding: 0.5rem 1rem;
    }
  }
  &--secondary &,
  &--print & {
    &__inner {
      background: $color-button-secondary;
      color: $color-text;
      font-weight: normal;
      .svg-icon {
        fill: $color-text;
      }
    }
  }
  &--print & {
    &__inner {
      .svg-icon {
        height: 1rem;
        width: 1rem;
      }
    }
  }
  // When hovered
  &:hover & {
    &__inner {
      background: darken($color-button, 5%);
    }
  }
  &--secondary:hover &,
  &--print:hover & {
    &__inner {
      background: darken($color-button-secondary, 5%);
    }
  }
  // Link button when hovered
  &--link:hover {
    text-decoration: none;
  }
  // Link button when focussed
  &--link:focus:not(:active):not(:hover) &__inner {
    background: $color-focus;
    color: $color-text-link-focus;
  }
  &--link:not(.btn--secondary) &,
  &--link:not(.btn--secondary):active &,
  &--link:not(.btn--secondary):hover & {
    &__inner {
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--link:focus:not(.btn--secondary) &,
  &--link:focus:hover:not(.btn--secondary) & {
    &__inner {
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--loader &__inner {
    position: relative;
    transition: color 0.3s ease-in-out;
    .svg-icon {
      fill: $color-white;
      height: 1.5rem;
      left: 50%;
      margin: 0;
      opacity: 0;
      position: absolute;
      top: 50%;
      transform: translate(-50%, -50%);
      transition: opacity 0.3s ease-in-out;
      width: 1.5rem;
    }
  }
  &--loader.is-loading &__inner {
    color: transparent;
    .svg-icon {
      opacity: 1;
    }
  }
  // Spooky Buttons
  &--ghost &,
  &--mobile & {
    &__inner {
      background: transparent;
      border: 2px solid rgba(255, 255, 255, 0.6);
      color: $color-white;
      .svg-icon {
        fill: $color-white;
      }
    }
  }
  &--ghost:active &,
  &--mobile:active & {
    &__inner {
      border-color: $color-white;
    }
  }
  &--ghost:active:focus {
    outline: 3px solid transparent;
  }
  &--ghost:focus:hover,
  &--mobile:focus:hover {
    outline: none;
  }
  &--ghost:hover &,
  &--mobile:hover & {
    &__inner {
      background: rgba(0, 0, 0, 0.1);
    }
  }
  &--ghost:focus &,
  &--mobile:focus & {
    &__inner {
      background-color: $color-focus;
      color: $color-text-link-focus;
      .svg-icon {
        fill: $color-black;
      }
    }
  }
  &--mobile[aria-expanded='true'] {
    .svg-icon {
      transform: rotate(270deg);
    }
  }
  &--mobile {
    .svg-icon {
      transform: rotate(90deg);
    }
    @include mq(m) {
      display: none;
    }
  }
  // Disabled buttons
  &--disabled {
    &:hover {
      cursor: not-allowed;
    }
    .btn__inner {
      opacity: 0.4;
    }
  }
}

Help improve this component

Let us know how we could improve this component or share your user research findings.

Discuss the ‘Button’ component on GitHub