Skip to main content

Input

The input component lets users enter alphanumeric characters for answers that are no longer than one line, using the appropriate keyboard on touch devices.

<div class="ons-field">
  <label class="ons-label  " for="text">Enter some text
  </label>
  <input type="text" id="text" class="ons-input ons-input--text ons-input-type__input" />
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "text",
        "label": {
            "text": "Enter some text"
        }
    })
}}
Name Type Required Description
id string true The id of the input. This will also be added to the label if a label is specified
type string false The type of the input, for example, number, email, tel. Will default to text
classes string false Classes to add to the input.
width string false Required width of the input. Will also accept the bp-suffix e.g. '7@m'
name string false The name of the input
value string | number false The value to set the input to
min number false Minimum accepted number or date
max number false Maximum accepted number or date
minLength number false Minimum accepted length of input value
maxLength number false Maximum accepted length of input value
attributes object false HTML attributes (for example, data attributes) to add to the input
label Label (ref) false Settings for the input label. for will automatically be set to match the input id
prefix InputPrefix false Settings to prefix the input with
suffix InputSuffix false Settings to suffix the input with
fieldId string false Id for the field
fieldClasses string false Classes for the field
dontWrap boolean false Prevents the input from being wrapped in a field component
mutuallyExclusive MutuallyExclusive (ref) false Configuration object if this is a mutually exclusive input
CharCheckLimit CharCheckLimit false Configuration object if this input has a character count
legend string Only if mutuallyExclusive is set Text content for the legend
legendClasses string false Classes for the legend
error Error (ref) false Configuration for validation errors
autocomplete string true Autocomplete attribute used to override the browsers native autocomplete
accessiblePlaceholder boolean false Will add the provided label as an accessible placeholder
searchButton Button (ref) false Settings for the button used for a search pattern.
postTextboxLinkText string false The text for the link to follow the textbox
postTextboxLinkUrl string false The url for the link to follow the textbox
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
required boolean false Adds the required attribute to the input to indicate that the user must specify a value for the input before the owning form can be submitted

Prefix/Suffix

Name Type Required Description
text string true The text for the prefix/suffix
title string true The title of the prefix/suffix. For example where text is “cm”, title would be “centimetres”
id string false Id for the prefix/suffix

CharCheckLimit

Name Type Required Description
limit number false The maximum amount of characters a user should type in
charcheckCountdown boolean false Displays the number of remaining characters allowed based on the limit
charCountPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining. {x} Will be replaced with the number
charCountSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining (singular). {x} Will be replaced with the number
charCountOverLimitSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (singular). {x} Will be replaced with the number
charCountOverLimitPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (plural). {x} Will be replaced with the number
{% macro onsInput(params) %}
    {% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %}
    {% from "components/char-check-limit/_macro.njk" import onsCharLimit %}
    {% from "components/search/_macro.njk" import onsSearch %}
    {% from "components/field/_macro.njk" import onsField %}
    {% from "components/label/_macro.njk" import onsLabel %}

    {% if params.type == "number" %}
        {# Type must be "text" or Firefox and Safari will set a blank value to the server if non numeric characters are entered -
        they don’t block non numeric characters: https://bugzilla.mozilla.org/show_bug.cgi?id=1398528 #}
        {% set type = "text" %}
        {% set pattern = "[0-9]*" %}
        {% set inputmode = "numeric" %}
    {% elif params.type is defined and params.type %}
        {% set type = params.type %}
    {% elif params.searchButton is defined and params.searchButton %}
        {% set type = "search" %}
    {% else %}
        {% set type = "text" %}
    {% endif %}

    {% set exclusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %}
    {% set inputPlaceholder = ' ons-input--placeholder' if params.accessiblePlaceholder else "" %}

    {% set input %}
        <input
            type="{{ type }}"
            id="{{ params.id }}"
            class="ons-input ons-input--text ons-input-type__input{% if params.error is defined and params.error %} ons-input--error{% endif %}{% if params.searchButton is defined and params.searchButton %} ons-search__input{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.width is defined and params.width %} ons-input{% if params.type is defined and (params.type === 'number' or params.type === 'tel') %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
            {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}title="{{ params.prefix.title if params.prefix }}{{ params.suffix.title if params.suffix }}"{% endif %}
            {% if params.name is defined and params.name %}name="{{ params.name }}"{% endif %}
            {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
            {% if params.accept is defined and params.accept %}accept="{{ params.accept }}"{% endif %}
            {% if params.min is defined and params.min %}min="{{ params.min }}"{% endif %}
            {% if params.max is defined and params.max %}max="{{ params.max }}"{% endif %}
            {% if params.minLength is defined and params.minLength %}minlength="{{ params.minLength }}"{% endif %}
            {% if params.maxLength is defined and params.maxLength %}maxlength="{{ params.maxLength }}"{% endif %}
            {% if pattern is defined and pattern %}pattern="{{ pattern }}"{% endif %}
            {% if inputmode is defined and inputmode %}inputmode="{{ inputmode }}"{% endif %}
            {% if params.autocomplete is defined and params.autocomplete %}autocomplete="{{ params.autocomplete }}"{% endif %}
            {% if params.accessiblePlaceholder is defined and params.accessiblePlaceholder %}placeholder="{{ params.label.text }}"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit %}data-char-check-ref="{{ params.id }}-check-remaining" data-char-check-num="{{ params.charCheckLimit.limit }}" aria-describedby="{{ params.id }}-check-remaining"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.charcheckCountdown is defined and params.charCheckLimit.charcheckCountdown %}data-char-check-countdown="true"{% 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 }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
            {% if params.label is defined and params.label and params.label.description is defined and params.label.description %}{% if params.label.id is defined and params.label.id %}aria-describedby="{{ params.label.id }}-description-hint"{% else %}aria-describedby="description-hint"{% endif %}{% endif %}
            {% if params.required is defined and params.required == true %}required="required"{% 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 %}
        {% if params.postTextboxLinkText is defined and params.postTextboxLinkText %}
            <a class="ons-u-fs-s" href="{{ params.postTextboxLinkUrl }}">{{ params.postTextboxLinkText }}</a>
        {% endif %}
    {% endset %}
    {% set field %}
        {% if params.label is defined and params.label and params.label.text is defined and params.label.text %}
            {{ onsLabel({
                "for": params.id,
                "id": params.label.id,
                "text": params.label.text,
                "classes": params.label.classes,
                "description": params.label.description,
                "attributes": params.label.attributes,
                "accessiblePlaceholder": params.accessiblePlaceholder
            }) }}
        {% endif %}

        {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}
            {% if params.prefix is defined and params.prefix %}
                {% set prefixClass = " ons-input-type--prefix" %}
            {% endif %}

            <span class="ons-input-type{{ prefixClass }}">
                <span class="ons-input-type__inner">
                    {{ input | safe }}
                    {% set abbr = params.prefix or params.suffix %}
                    <abbr
                        class="ons-input-type__type ons-js-input-abbr"
                        aria-hidden="true"
                        title="{{ abbr.title }}"
                        {% if abbr.id is defined and abbr.id %} id="{{ abbr.id }}"{% endif %}
                        >{{ abbr.text or abbr.title }}</abbr>
                </span>
            </span>
        {% elif params.searchButton is defined and params.searchButton %}
            <span class="ons-grid--flex ons-search">
                {% call onsSearch({
                    "accessiblePlaceholder": params.accessiblePlaceholder,
                    "searchButton": {
                        "type": params.searchButton.type,
                        "text": params.searchButton.text,
                        "id": params.searchButton.id,
                        "attributes": params.searchButton.attributes,
                        "classes": params.searchButton.classes
                    }
                }) %}
                    {{ input | safe }}
                {% endcall %}
            </span>
        {% else %}
            {{ input | safe }}
        {% endif %}
    {% endset %}

    {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
        {% set charCheckField %}
            {% call onsCharLimit({
                "id": params.id ~ "-check",
                "limit": params.charCheckLimit.limit,
                "type": "check",
                "charCountSingular": params.charCheckLimit.charCountSingular,
                "charCountPlural": params.charCheckLimit.charCountPlural,
                "charCountOverLimitSingular": params.charCheckLimit.charCountOverLimitSingular,
                "charCountOverLimitPlural": params.charCheckLimit.charCountOverLimitPlural
            }) %}
                {{ field | safe }}
            {% endcall %}
        {% endset %}
    {% endif %}

    {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
        {% call onsMutuallyExclusive({
            "id": params.fieldId,
            "legend": params.legend,
            "legendClasses": params.legendClasses ~ "ons-js-input-legend",
            "description": params.description,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "checkbox": params.mutuallyExclusive.checkbox,
            "or": params.mutuallyExclusive.or,
            "deselectMessage": params.mutuallyExclusive.deselectMessage,
            "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective,
            "deselectCheckboxAdjective": params.mutuallyExclusive.deselectCheckboxAdjective,
            "error": params.error,
            "autosuggestResults": params.autosuggestResults
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% elif type == "hidden" %}
        {{ field | safe }}
    {% else %}
        {% call onsField({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "error": params.error
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% endif %}
{% endmacro %}
.ons-input-type {
  display: block;

  // Keep the entire component display block, but use inline-flex on inner to prevent the orange focus from going full width
  &__inner {
    display: inline-flex;
    position: relative;
  }

  // Double ampersand is needed to solve specificity issues
  & &__input {
    flex: 1 1 auto;
    position: relative;
    z-index: 1;

    &:focus {
      box-shadow: inset 0 0 0 1px $color-input;
    }
  }

  &__type[title] {
    background-color: $color-button-secondary;
    border: 1px solid $color-input;
    display: block;
    flex: 0 0 auto;
    font-size: 1rem;
    font-weight: $font-weight-bold;
    line-height: normal;
    padding: $input-padding-vertical $input-padding-horizontal * 2;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
  }

  &__input:focus + &__type[title]::after {
    border-radius: $input-radius;
    bottom: 0;
    box-shadow: 0 0 0 3px $color-focus;
    content: '';
    display: block;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }

  &:not(&--prefix) & {
    &__type[title] {
      border-left: 0;
      border-radius: 0 $input-radius $input-radius 0;
    }

    &__input {
      border-radius: $input-radius 0 0 $input-radius;
    }
  }

  &--prefix & {
    &__type[title] {
      border-radius: $input-radius 0 0 $input-radius;
      border-right: 0;
      order: 0;
    }

    &__input {
      border-radius: 0 $input-radius $input-radius 0;
      order: 1;
    }
  }
}

.ons-input {
  border: $input-border-width solid $color-input;
  border-radius: $input-radius;
  color: inherit;
  display: block;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1rem;
  padding: $input-padding-vertical $input-padding-horizontal;
  position: relative;
  width: 100%;
  z-index: 3;

  &::-ms-clear {
    display: none;
  }

  @include mq(s) {
    &--text,
    &--select {
      &:not(.ons-input--block):not([class*='input--w-']) {
        width: $input-width;
      }
    }
  }

  &--text,
  &--textarea {
    // Prevent inner shadow on iOS
    appearance: none;
  }

  &:focus {
    box-shadow: 0 0 0 3px $color-focus, inset 0 0 0 1px $color-input;
    outline: none;
  }

  &:disabled {
    border-color: $color-grey-75;
    cursor: not-allowed;
  }

  &--error:not(:focus) {
    border: 1px solid $color-ruby-red;
    box-shadow: inset 0 0 0 1px $color-ruby-red;
  }

  &--with-description {
    margin-bottom: 0.55rem;
  }
}

// Text input widths
@include input-width('ons-input--w-{x}');

// Number input widths
@include input-width('ons-input-number--w-{x}', 0.54rem);

.ons-input--postcode {
  max-width: input-width-calc($chars: 5, $num-chars: 2, $spaces: 1);
  width: 100%;
}

.ons-input__helper {
  font-size: 0.8rem;
  font-weight: $font-weight-bold;
  margin-top: 0.2rem;
}

.ons-input--select {
  appearance: none;
  background: $color-white url('#{$static}/img/icons--chevron-down.svg') no-repeat center right 10px;
  background-size: 1rem;
  line-height: 1.3rem;
  padding: 0.39rem 2rem 0.39rem $input-padding-horizontal;

  &::-ms-expand {
    display: none;
  }
}

.ons-input--textarea {
  line-height: normal;
  resize: vertical;
  width: 100%;
}

.ons-input--block {
  display: block;
  width: 100%;
}

.ons-input--placeholder {
  background: transparent;
  &::placeholder {
    color: transparent;
  }
  &:valid:not(:placeholder-shown) {
    background-color: $color-white;
  }
  &:focus {
    background-color: $color-white;
  }
}

.ons-input--limit-reached:not(:focus) {
  border: $input-border-width solid $color-ruby-red;
}

.ons-input__limit {
  display: block;

  &--reached {
    color: $color-ruby-red;
  }
}

.ons-input--ghost {
  border: 2px solid rgba(255, 255, 255, 0.6);
  &:focus {
    border: 2px solid $color-input;
  }
}

.ons-input-search {
  @extend .ons-input--block;

  &--icon {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    background-position: 9px 7px;
    background-repeat: no-repeat;
    background-size: 24px 24px;
    padding-left: 2.4rem;

    &:focus,
    &:active,
    &:valid:not(:placeholder-shown) {
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 9px 7px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

When to use this component

Use the input component when you need to let users enter alphanumeric information that is no longer than one line, such as their telephone number or email address.

There is specific guidance on how to ask for:

When not to use this component

Do not use this component if you need to let users enter information that will span more than one line. In this case, you should use the textarea component.

How to use this component

All input components must have a visible label.

Inputs should be appropriately sized to help the user understand what they should enter by making them the right size for the content they are intended for.

By default, the input component will fill the width of its container up to 500px viewport width, then limited to 20rem. A width constraint class can be used to make the input smaller.

Autocomplete

Use the autocomplete attribute on inputs to help the user fill out a form more quickly. This attribute lets you specify a purpose for the input so the user’s browser can autofill the information for them.

You will need to use the autocomplete attribute for each applicable input purpose  to meet AA WCAG 2.1 accessibility requirements.

Variants

Input with description

An optional description parameter can be provided inside the label object to display a hint to help the user enter their information.

<div class="ons-field">
  <label class="ons-label  ons-label--with-description " for="text">Enter some text
  </label>
  <span id="description-hint" class="ons-label__description  ons-input--with-description">
    Example description hint text
  </span>
  <input type="text" id="text" class="ons-input ons-input--text ons-input-type__input" aria-describedby="description-hint" />
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "text",
        "label": {
            "text": "Enter some text",
            "description": "Example description hint text"
        }
    })
}}
Name Type Required Description
id string true The id of the input. This will also be added to the label if a label is specified
type string false The type of the input, for example, number, email, tel. Will default to text
classes string false Classes to add to the input.
width string false Required width of the input. Will also accept the bp-suffix e.g. '7@m'
name string false The name of the input
value string | number false The value to set the input to
min number false Minimum accepted number or date
max number false Maximum accepted number or date
minLength number false Minimum accepted length of input value
maxLength number false Maximum accepted length of input value
attributes object false HTML attributes (for example, data attributes) to add to the input
label Label (ref) false Settings for the input label. for will automatically be set to match the input id
prefix InputPrefix false Settings to prefix the input with
suffix InputSuffix false Settings to suffix the input with
fieldId string false Id for the field
fieldClasses string false Classes for the field
dontWrap boolean false Prevents the input from being wrapped in a field component
mutuallyExclusive MutuallyExclusive (ref) false Configuration object if this is a mutually exclusive input
CharCheckLimit CharCheckLimit false Configuration object if this input has a character count
legend string Only if mutuallyExclusive is set Text content for the legend
legendClasses string false Classes for the legend
error Error (ref) false Configuration for validation errors
autocomplete string true Autocomplete attribute used to override the browsers native autocomplete
accessiblePlaceholder boolean false Will add the provided label as an accessible placeholder
searchButton Button (ref) false Settings for the button used for a search pattern.
postTextboxLinkText string false The text for the link to follow the textbox
postTextboxLinkUrl string false The url for the link to follow the textbox
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
required boolean false Adds the required attribute to the input to indicate that the user must specify a value for the input before the owning form can be submitted

Prefix/Suffix

Name Type Required Description
text string true The text for the prefix/suffix
title string true The title of the prefix/suffix. For example where text is “cm”, title would be “centimetres”
id string false Id for the prefix/suffix

CharCheckLimit

Name Type Required Description
limit number false The maximum amount of characters a user should type in
charcheckCountdown boolean false Displays the number of remaining characters allowed based on the limit
charCountPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining. {x} Will be replaced with the number
charCountSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining (singular). {x} Will be replaced with the number
charCountOverLimitSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (singular). {x} Will be replaced with the number
charCountOverLimitPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (plural). {x} Will be replaced with the number
{% macro onsInput(params) %}
    {% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %}
    {% from "components/char-check-limit/_macro.njk" import onsCharLimit %}
    {% from "components/search/_macro.njk" import onsSearch %}
    {% from "components/field/_macro.njk" import onsField %}
    {% from "components/label/_macro.njk" import onsLabel %}

    {% if params.type == "number" %}
        {# Type must be "text" or Firefox and Safari will set a blank value to the server if non numeric characters are entered -
        they don’t block non numeric characters: https://bugzilla.mozilla.org/show_bug.cgi?id=1398528 #}
        {% set type = "text" %}
        {% set pattern = "[0-9]*" %}
        {% set inputmode = "numeric" %}
    {% elif params.type is defined and params.type %}
        {% set type = params.type %}
    {% elif params.searchButton is defined and params.searchButton %}
        {% set type = "search" %}
    {% else %}
        {% set type = "text" %}
    {% endif %}

    {% set exclusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %}
    {% set inputPlaceholder = ' ons-input--placeholder' if params.accessiblePlaceholder else "" %}

    {% set input %}
        <input
            type="{{ type }}"
            id="{{ params.id }}"
            class="ons-input ons-input--text ons-input-type__input{% if params.error is defined and params.error %} ons-input--error{% endif %}{% if params.searchButton is defined and params.searchButton %} ons-search__input{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.width is defined and params.width %} ons-input{% if params.type is defined and (params.type === 'number' or params.type === 'tel') %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
            {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}title="{{ params.prefix.title if params.prefix }}{{ params.suffix.title if params.suffix }}"{% endif %}
            {% if params.name is defined and params.name %}name="{{ params.name }}"{% endif %}
            {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
            {% if params.accept is defined and params.accept %}accept="{{ params.accept }}"{% endif %}
            {% if params.min is defined and params.min %}min="{{ params.min }}"{% endif %}
            {% if params.max is defined and params.max %}max="{{ params.max }}"{% endif %}
            {% if params.minLength is defined and params.minLength %}minlength="{{ params.minLength }}"{% endif %}
            {% if params.maxLength is defined and params.maxLength %}maxlength="{{ params.maxLength }}"{% endif %}
            {% if pattern is defined and pattern %}pattern="{{ pattern }}"{% endif %}
            {% if inputmode is defined and inputmode %}inputmode="{{ inputmode }}"{% endif %}
            {% if params.autocomplete is defined and params.autocomplete %}autocomplete="{{ params.autocomplete }}"{% endif %}
            {% if params.accessiblePlaceholder is defined and params.accessiblePlaceholder %}placeholder="{{ params.label.text }}"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit %}data-char-check-ref="{{ params.id }}-check-remaining" data-char-check-num="{{ params.charCheckLimit.limit }}" aria-describedby="{{ params.id }}-check-remaining"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.charcheckCountdown is defined and params.charCheckLimit.charcheckCountdown %}data-char-check-countdown="true"{% 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 }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
            {% if params.label is defined and params.label and params.label.description is defined and params.label.description %}{% if params.label.id is defined and params.label.id %}aria-describedby="{{ params.label.id }}-description-hint"{% else %}aria-describedby="description-hint"{% endif %}{% endif %}
            {% if params.required is defined and params.required == true %}required="required"{% 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 %}
        {% if params.postTextboxLinkText is defined and params.postTextboxLinkText %}
            <a class="ons-u-fs-s" href="{{ params.postTextboxLinkUrl }}">{{ params.postTextboxLinkText }}</a>
        {% endif %}
    {% endset %}
    {% set field %}
        {% if params.label is defined and params.label and params.label.text is defined and params.label.text %}
            {{ onsLabel({
                "for": params.id,
                "id": params.label.id,
                "text": params.label.text,
                "classes": params.label.classes,
                "description": params.label.description,
                "attributes": params.label.attributes,
                "accessiblePlaceholder": params.accessiblePlaceholder
            }) }}
        {% endif %}

        {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}
            {% if params.prefix is defined and params.prefix %}
                {% set prefixClass = " ons-input-type--prefix" %}
            {% endif %}

            <span class="ons-input-type{{ prefixClass }}">
                <span class="ons-input-type__inner">
                    {{ input | safe }}
                    {% set abbr = params.prefix or params.suffix %}
                    <abbr
                        class="ons-input-type__type ons-js-input-abbr"
                        aria-hidden="true"
                        title="{{ abbr.title }}"
                        {% if abbr.id is defined and abbr.id %} id="{{ abbr.id }}"{% endif %}
                        >{{ abbr.text or abbr.title }}</abbr>
                </span>
            </span>
        {% elif params.searchButton is defined and params.searchButton %}
            <span class="ons-grid--flex ons-search">
                {% call onsSearch({
                    "accessiblePlaceholder": params.accessiblePlaceholder,
                    "searchButton": {
                        "type": params.searchButton.type,
                        "text": params.searchButton.text,
                        "id": params.searchButton.id,
                        "attributes": params.searchButton.attributes,
                        "classes": params.searchButton.classes
                    }
                }) %}
                    {{ input | safe }}
                {% endcall %}
            </span>
        {% else %}
            {{ input | safe }}
        {% endif %}
    {% endset %}

    {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
        {% set charCheckField %}
            {% call onsCharLimit({
                "id": params.id ~ "-check",
                "limit": params.charCheckLimit.limit,
                "type": "check",
                "charCountSingular": params.charCheckLimit.charCountSingular,
                "charCountPlural": params.charCheckLimit.charCountPlural,
                "charCountOverLimitSingular": params.charCheckLimit.charCountOverLimitSingular,
                "charCountOverLimitPlural": params.charCheckLimit.charCountOverLimitPlural
            }) %}
                {{ field | safe }}
            {% endcall %}
        {% endset %}
    {% endif %}

    {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
        {% call onsMutuallyExclusive({
            "id": params.fieldId,
            "legend": params.legend,
            "legendClasses": params.legendClasses ~ "ons-js-input-legend",
            "description": params.description,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "checkbox": params.mutuallyExclusive.checkbox,
            "or": params.mutuallyExclusive.or,
            "deselectMessage": params.mutuallyExclusive.deselectMessage,
            "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective,
            "deselectCheckboxAdjective": params.mutuallyExclusive.deselectCheckboxAdjective,
            "error": params.error,
            "autosuggestResults": params.autosuggestResults
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% elif type == "hidden" %}
        {{ field | safe }}
    {% else %}
        {% call onsField({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "error": params.error
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% endif %}
{% endmacro %}
.ons-input-type {
  display: block;

  // Keep the entire component display block, but use inline-flex on inner to prevent the orange focus from going full width
  &__inner {
    display: inline-flex;
    position: relative;
  }

  // Double ampersand is needed to solve specificity issues
  & &__input {
    flex: 1 1 auto;
    position: relative;
    z-index: 1;

    &:focus {
      box-shadow: inset 0 0 0 1px $color-input;
    }
  }

  &__type[title] {
    background-color: $color-button-secondary;
    border: 1px solid $color-input;
    display: block;
    flex: 0 0 auto;
    font-size: 1rem;
    font-weight: $font-weight-bold;
    line-height: normal;
    padding: $input-padding-vertical $input-padding-horizontal * 2;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
  }

  &__input:focus + &__type[title]::after {
    border-radius: $input-radius;
    bottom: 0;
    box-shadow: 0 0 0 3px $color-focus;
    content: '';
    display: block;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }

  &:not(&--prefix) & {
    &__type[title] {
      border-left: 0;
      border-radius: 0 $input-radius $input-radius 0;
    }

    &__input {
      border-radius: $input-radius 0 0 $input-radius;
    }
  }

  &--prefix & {
    &__type[title] {
      border-radius: $input-radius 0 0 $input-radius;
      border-right: 0;
      order: 0;
    }

    &__input {
      border-radius: 0 $input-radius $input-radius 0;
      order: 1;
    }
  }
}

.ons-input {
  border: $input-border-width solid $color-input;
  border-radius: $input-radius;
  color: inherit;
  display: block;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1rem;
  padding: $input-padding-vertical $input-padding-horizontal;
  position: relative;
  width: 100%;
  z-index: 3;

  &::-ms-clear {
    display: none;
  }

  @include mq(s) {
    &--text,
    &--select {
      &:not(.ons-input--block):not([class*='input--w-']) {
        width: $input-width;
      }
    }
  }

  &--text,
  &--textarea {
    // Prevent inner shadow on iOS
    appearance: none;
  }

  &:focus {
    box-shadow: 0 0 0 3px $color-focus, inset 0 0 0 1px $color-input;
    outline: none;
  }

  &:disabled {
    border-color: $color-grey-75;
    cursor: not-allowed;
  }

  &--error:not(:focus) {
    border: 1px solid $color-ruby-red;
    box-shadow: inset 0 0 0 1px $color-ruby-red;
  }

  &--with-description {
    margin-bottom: 0.55rem;
  }
}

// Text input widths
@include input-width('ons-input--w-{x}');

// Number input widths
@include input-width('ons-input-number--w-{x}', 0.54rem);

.ons-input--postcode {
  max-width: input-width-calc($chars: 5, $num-chars: 2, $spaces: 1);
  width: 100%;
}

.ons-input__helper {
  font-size: 0.8rem;
  font-weight: $font-weight-bold;
  margin-top: 0.2rem;
}

.ons-input--select {
  appearance: none;
  background: $color-white url('#{$static}/img/icons--chevron-down.svg') no-repeat center right 10px;
  background-size: 1rem;
  line-height: 1.3rem;
  padding: 0.39rem 2rem 0.39rem $input-padding-horizontal;

  &::-ms-expand {
    display: none;
  }
}

.ons-input--textarea {
  line-height: normal;
  resize: vertical;
  width: 100%;
}

.ons-input--block {
  display: block;
  width: 100%;
}

.ons-input--placeholder {
  background: transparent;
  &::placeholder {
    color: transparent;
  }
  &:valid:not(:placeholder-shown) {
    background-color: $color-white;
  }
  &:focus {
    background-color: $color-white;
  }
}

.ons-input--limit-reached:not(:focus) {
  border: $input-border-width solid $color-ruby-red;
}

.ons-input__limit {
  display: block;

  &--reached {
    color: $color-ruby-red;
  }
}

.ons-input--ghost {
  border: 2px solid rgba(255, 255, 255, 0.6);
  &:focus {
    border: 2px solid $color-input;
  }
}

.ons-input-search {
  @extend .ons-input--block;

  &--icon {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    background-position: 9px 7px;
    background-repeat: no-repeat;
    background-size: 24px 24px;
    padding-left: 2.4rem;

    &:focus,
    &:active,
    &:valid:not(:placeholder-shown) {
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 9px 7px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

Width constrained

Change the width of inputs to suit the type of content. This helps users understand what they need to enter.

To calculate the width to constrain to, you need to set a type and a width value. The type will determine if the input will contain letters or numbers: tel, number, email or text. The values you set will determine how wide the input is. For example, if you set type: number and width: 4, this will constrain the input to the width of 4 numbers. If you do not specify a type, ‘text’ is the default.

The width values available for both types are: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 30, 40, 50.

For inputs that will contain both letters and numbers, you will need to use an appropriate number value. For example, UK postcodes always consist of up to 7 letters and numbers and one optional space separating the outward and inward codes. So using the width 7 will create a width appropriate for the longest potential postcode “WW4W 4WW”.

<div class="ons-field">
  <label class="ons-label  " for="text-width">Postcode
  </label>
  <input type="text" id="text-width" class="ons-input ons-input--text ons-input-type__input ons-input--w-7" autocomplete="postal-code" />
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "text-width",
        "width": "7",
        "autocomplete": "postal-code",
        "label": {
            "text": "Postcode"
        }
    })
}}
Name Type Required Description
id string true The id of the input. This will also be added to the label if a label is specified
type string false The type of the input, for example, number, email, tel. Will default to text
classes string false Classes to add to the input.
width string false Required width of the input. Will also accept the bp-suffix e.g. '7@m'
name string false The name of the input
value string | number false The value to set the input to
min number false Minimum accepted number or date
max number false Maximum accepted number or date
minLength number false Minimum accepted length of input value
maxLength number false Maximum accepted length of input value
attributes object false HTML attributes (for example, data attributes) to add to the input
label Label (ref) false Settings for the input label. for will automatically be set to match the input id
prefix InputPrefix false Settings to prefix the input with
suffix InputSuffix false Settings to suffix the input with
fieldId string false Id for the field
fieldClasses string false Classes for the field
dontWrap boolean false Prevents the input from being wrapped in a field component
mutuallyExclusive MutuallyExclusive (ref) false Configuration object if this is a mutually exclusive input
CharCheckLimit CharCheckLimit false Configuration object if this input has a character count
legend string Only if mutuallyExclusive is set Text content for the legend
legendClasses string false Classes for the legend
error Error (ref) false Configuration for validation errors
autocomplete string true Autocomplete attribute used to override the browsers native autocomplete
accessiblePlaceholder boolean false Will add the provided label as an accessible placeholder
searchButton Button (ref) false Settings for the button used for a search pattern.
postTextboxLinkText string false The text for the link to follow the textbox
postTextboxLinkUrl string false The url for the link to follow the textbox
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
required boolean false Adds the required attribute to the input to indicate that the user must specify a value for the input before the owning form can be submitted

Prefix/Suffix

Name Type Required Description
text string true The text for the prefix/suffix
title string true The title of the prefix/suffix. For example where text is “cm”, title would be “centimetres”
id string false Id for the prefix/suffix

CharCheckLimit

Name Type Required Description
limit number false The maximum amount of characters a user should type in
charcheckCountdown boolean false Displays the number of remaining characters allowed based on the limit
charCountPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining. {x} Will be replaced with the number
charCountSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining (singular). {x} Will be replaced with the number
charCountOverLimitSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (singular). {x} Will be replaced with the number
charCountOverLimitPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (plural). {x} Will be replaced with the number
{% macro onsInput(params) %}
    {% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %}
    {% from "components/char-check-limit/_macro.njk" import onsCharLimit %}
    {% from "components/search/_macro.njk" import onsSearch %}
    {% from "components/field/_macro.njk" import onsField %}
    {% from "components/label/_macro.njk" import onsLabel %}

    {% if params.type == "number" %}
        {# Type must be "text" or Firefox and Safari will set a blank value to the server if non numeric characters are entered -
        they don’t block non numeric characters: https://bugzilla.mozilla.org/show_bug.cgi?id=1398528 #}
        {% set type = "text" %}
        {% set pattern = "[0-9]*" %}
        {% set inputmode = "numeric" %}
    {% elif params.type is defined and params.type %}
        {% set type = params.type %}
    {% elif params.searchButton is defined and params.searchButton %}
        {% set type = "search" %}
    {% else %}
        {% set type = "text" %}
    {% endif %}

    {% set exclusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %}
    {% set inputPlaceholder = ' ons-input--placeholder' if params.accessiblePlaceholder else "" %}

    {% set input %}
        <input
            type="{{ type }}"
            id="{{ params.id }}"
            class="ons-input ons-input--text ons-input-type__input{% if params.error is defined and params.error %} ons-input--error{% endif %}{% if params.searchButton is defined and params.searchButton %} ons-search__input{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.width is defined and params.width %} ons-input{% if params.type is defined and (params.type === 'number' or params.type === 'tel') %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
            {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}title="{{ params.prefix.title if params.prefix }}{{ params.suffix.title if params.suffix }}"{% endif %}
            {% if params.name is defined and params.name %}name="{{ params.name }}"{% endif %}
            {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
            {% if params.accept is defined and params.accept %}accept="{{ params.accept }}"{% endif %}
            {% if params.min is defined and params.min %}min="{{ params.min }}"{% endif %}
            {% if params.max is defined and params.max %}max="{{ params.max }}"{% endif %}
            {% if params.minLength is defined and params.minLength %}minlength="{{ params.minLength }}"{% endif %}
            {% if params.maxLength is defined and params.maxLength %}maxlength="{{ params.maxLength }}"{% endif %}
            {% if pattern is defined and pattern %}pattern="{{ pattern }}"{% endif %}
            {% if inputmode is defined and inputmode %}inputmode="{{ inputmode }}"{% endif %}
            {% if params.autocomplete is defined and params.autocomplete %}autocomplete="{{ params.autocomplete }}"{% endif %}
            {% if params.accessiblePlaceholder is defined and params.accessiblePlaceholder %}placeholder="{{ params.label.text }}"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit %}data-char-check-ref="{{ params.id }}-check-remaining" data-char-check-num="{{ params.charCheckLimit.limit }}" aria-describedby="{{ params.id }}-check-remaining"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.charcheckCountdown is defined and params.charCheckLimit.charcheckCountdown %}data-char-check-countdown="true"{% 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 }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
            {% if params.label is defined and params.label and params.label.description is defined and params.label.description %}{% if params.label.id is defined and params.label.id %}aria-describedby="{{ params.label.id }}-description-hint"{% else %}aria-describedby="description-hint"{% endif %}{% endif %}
            {% if params.required is defined and params.required == true %}required="required"{% 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 %}
        {% if params.postTextboxLinkText is defined and params.postTextboxLinkText %}
            <a class="ons-u-fs-s" href="{{ params.postTextboxLinkUrl }}">{{ params.postTextboxLinkText }}</a>
        {% endif %}
    {% endset %}
    {% set field %}
        {% if params.label is defined and params.label and params.label.text is defined and params.label.text %}
            {{ onsLabel({
                "for": params.id,
                "id": params.label.id,
                "text": params.label.text,
                "classes": params.label.classes,
                "description": params.label.description,
                "attributes": params.label.attributes,
                "accessiblePlaceholder": params.accessiblePlaceholder
            }) }}
        {% endif %}

        {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}
            {% if params.prefix is defined and params.prefix %}
                {% set prefixClass = " ons-input-type--prefix" %}
            {% endif %}

            <span class="ons-input-type{{ prefixClass }}">
                <span class="ons-input-type__inner">
                    {{ input | safe }}
                    {% set abbr = params.prefix or params.suffix %}
                    <abbr
                        class="ons-input-type__type ons-js-input-abbr"
                        aria-hidden="true"
                        title="{{ abbr.title }}"
                        {% if abbr.id is defined and abbr.id %} id="{{ abbr.id }}"{% endif %}
                        >{{ abbr.text or abbr.title }}</abbr>
                </span>
            </span>
        {% elif params.searchButton is defined and params.searchButton %}
            <span class="ons-grid--flex ons-search">
                {% call onsSearch({
                    "accessiblePlaceholder": params.accessiblePlaceholder,
                    "searchButton": {
                        "type": params.searchButton.type,
                        "text": params.searchButton.text,
                        "id": params.searchButton.id,
                        "attributes": params.searchButton.attributes,
                        "classes": params.searchButton.classes
                    }
                }) %}
                    {{ input | safe }}
                {% endcall %}
            </span>
        {% else %}
            {{ input | safe }}
        {% endif %}
    {% endset %}

    {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
        {% set charCheckField %}
            {% call onsCharLimit({
                "id": params.id ~ "-check",
                "limit": params.charCheckLimit.limit,
                "type": "check",
                "charCountSingular": params.charCheckLimit.charCountSingular,
                "charCountPlural": params.charCheckLimit.charCountPlural,
                "charCountOverLimitSingular": params.charCheckLimit.charCountOverLimitSingular,
                "charCountOverLimitPlural": params.charCheckLimit.charCountOverLimitPlural
            }) %}
                {{ field | safe }}
            {% endcall %}
        {% endset %}
    {% endif %}

    {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
        {% call onsMutuallyExclusive({
            "id": params.fieldId,
            "legend": params.legend,
            "legendClasses": params.legendClasses ~ "ons-js-input-legend",
            "description": params.description,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "checkbox": params.mutuallyExclusive.checkbox,
            "or": params.mutuallyExclusive.or,
            "deselectMessage": params.mutuallyExclusive.deselectMessage,
            "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective,
            "deselectCheckboxAdjective": params.mutuallyExclusive.deselectCheckboxAdjective,
            "error": params.error,
            "autosuggestResults": params.autosuggestResults
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% elif type == "hidden" %}
        {{ field | safe }}
    {% else %}
        {% call onsField({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "error": params.error
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% endif %}
{% endmacro %}
.ons-input-type {
  display: block;

  // Keep the entire component display block, but use inline-flex on inner to prevent the orange focus from going full width
  &__inner {
    display: inline-flex;
    position: relative;
  }

  // Double ampersand is needed to solve specificity issues
  & &__input {
    flex: 1 1 auto;
    position: relative;
    z-index: 1;

    &:focus {
      box-shadow: inset 0 0 0 1px $color-input;
    }
  }

  &__type[title] {
    background-color: $color-button-secondary;
    border: 1px solid $color-input;
    display: block;
    flex: 0 0 auto;
    font-size: 1rem;
    font-weight: $font-weight-bold;
    line-height: normal;
    padding: $input-padding-vertical $input-padding-horizontal * 2;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
  }

  &__input:focus + &__type[title]::after {
    border-radius: $input-radius;
    bottom: 0;
    box-shadow: 0 0 0 3px $color-focus;
    content: '';
    display: block;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }

  &:not(&--prefix) & {
    &__type[title] {
      border-left: 0;
      border-radius: 0 $input-radius $input-radius 0;
    }

    &__input {
      border-radius: $input-radius 0 0 $input-radius;
    }
  }

  &--prefix & {
    &__type[title] {
      border-radius: $input-radius 0 0 $input-radius;
      border-right: 0;
      order: 0;
    }

    &__input {
      border-radius: 0 $input-radius $input-radius 0;
      order: 1;
    }
  }
}

.ons-input {
  border: $input-border-width solid $color-input;
  border-radius: $input-radius;
  color: inherit;
  display: block;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1rem;
  padding: $input-padding-vertical $input-padding-horizontal;
  position: relative;
  width: 100%;
  z-index: 3;

  &::-ms-clear {
    display: none;
  }

  @include mq(s) {
    &--text,
    &--select {
      &:not(.ons-input--block):not([class*='input--w-']) {
        width: $input-width;
      }
    }
  }

  &--text,
  &--textarea {
    // Prevent inner shadow on iOS
    appearance: none;
  }

  &:focus {
    box-shadow: 0 0 0 3px $color-focus, inset 0 0 0 1px $color-input;
    outline: none;
  }

  &:disabled {
    border-color: $color-grey-75;
    cursor: not-allowed;
  }

  &--error:not(:focus) {
    border: 1px solid $color-ruby-red;
    box-shadow: inset 0 0 0 1px $color-ruby-red;
  }

  &--with-description {
    margin-bottom: 0.55rem;
  }
}

// Text input widths
@include input-width('ons-input--w-{x}');

// Number input widths
@include input-width('ons-input-number--w-{x}', 0.54rem);

.ons-input--postcode {
  max-width: input-width-calc($chars: 5, $num-chars: 2, $spaces: 1);
  width: 100%;
}

.ons-input__helper {
  font-size: 0.8rem;
  font-weight: $font-weight-bold;
  margin-top: 0.2rem;
}

.ons-input--select {
  appearance: none;
  background: $color-white url('#{$static}/img/icons--chevron-down.svg') no-repeat center right 10px;
  background-size: 1rem;
  line-height: 1.3rem;
  padding: 0.39rem 2rem 0.39rem $input-padding-horizontal;

  &::-ms-expand {
    display: none;
  }
}

.ons-input--textarea {
  line-height: normal;
  resize: vertical;
  width: 100%;
}

.ons-input--block {
  display: block;
  width: 100%;
}

.ons-input--placeholder {
  background: transparent;
  &::placeholder {
    color: transparent;
  }
  &:valid:not(:placeholder-shown) {
    background-color: $color-white;
  }
  &:focus {
    background-color: $color-white;
  }
}

.ons-input--limit-reached:not(:focus) {
  border: $input-border-width solid $color-ruby-red;
}

.ons-input__limit {
  display: block;

  &--reached {
    color: $color-ruby-red;
  }
}

.ons-input--ghost {
  border: 2px solid rgba(255, 255, 255, 0.6);
  &:focus {
    border: 2px solid $color-input;
  }
}

.ons-input-search {
  @extend .ons-input--block;

  &--icon {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    background-position: 9px 7px;
    background-repeat: no-repeat;
    background-size: 24px 24px;
    padding-left: 2.4rem;

    &:focus,
    &:active,
    &:valid:not(:placeholder-shown) {
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 9px 7px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

There is specific guidance on how to ask users for addresses.

Email

Setting the type to email will bring up the correct keypad on touch devices.

<div class="ons-field">
  <label class="ons-label  ons-label--with-description " for="email">Email address
  </label>
  <span id="description-hint" class="ons-label__description  ons-input--with-description">
    This will not be stored and only used once to send your confirmation
  </span>
  <input type="email" id="email" class="ons-input ons-input--text ons-input-type__input" autocomplete="email" aria-describedby="description-hint" />
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "email",
        "type": "email",
        "autocomplete": "email",
        "label": {
            "text": "Email address",
            "description": "This will not be stored and only used once to send your confirmation"
        }
    })
}}
Name Type Required Description
id string true The id of the input. This will also be added to the label if a label is specified
type string false The type of the input, for example, number, email, tel. Will default to text
classes string false Classes to add to the input.
width string false Required width of the input. Will also accept the bp-suffix e.g. '7@m'
name string false The name of the input
value string | number false The value to set the input to
min number false Minimum accepted number or date
max number false Maximum accepted number or date
minLength number false Minimum accepted length of input value
maxLength number false Maximum accepted length of input value
attributes object false HTML attributes (for example, data attributes) to add to the input
label Label (ref) false Settings for the input label. for will automatically be set to match the input id
prefix InputPrefix false Settings to prefix the input with
suffix InputSuffix false Settings to suffix the input with
fieldId string false Id for the field
fieldClasses string false Classes for the field
dontWrap boolean false Prevents the input from being wrapped in a field component
mutuallyExclusive MutuallyExclusive (ref) false Configuration object if this is a mutually exclusive input
CharCheckLimit CharCheckLimit false Configuration object if this input has a character count
legend string Only if mutuallyExclusive is set Text content for the legend
legendClasses string false Classes for the legend
error Error (ref) false Configuration for validation errors
autocomplete string true Autocomplete attribute used to override the browsers native autocomplete
accessiblePlaceholder boolean false Will add the provided label as an accessible placeholder
searchButton Button (ref) false Settings for the button used for a search pattern.
postTextboxLinkText string false The text for the link to follow the textbox
postTextboxLinkUrl string false The url for the link to follow the textbox
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
required boolean false Adds the required attribute to the input to indicate that the user must specify a value for the input before the owning form can be submitted

Prefix/Suffix

Name Type Required Description
text string true The text for the prefix/suffix
title string true The title of the prefix/suffix. For example where text is “cm”, title would be “centimetres”
id string false Id for the prefix/suffix

CharCheckLimit

Name Type Required Description
limit number false The maximum amount of characters a user should type in
charcheckCountdown boolean false Displays the number of remaining characters allowed based on the limit
charCountPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining. {x} Will be replaced with the number
charCountSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining (singular). {x} Will be replaced with the number
charCountOverLimitSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (singular). {x} Will be replaced with the number
charCountOverLimitPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (plural). {x} Will be replaced with the number
{% macro onsInput(params) %}
    {% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %}
    {% from "components/char-check-limit/_macro.njk" import onsCharLimit %}
    {% from "components/search/_macro.njk" import onsSearch %}
    {% from "components/field/_macro.njk" import onsField %}
    {% from "components/label/_macro.njk" import onsLabel %}

    {% if params.type == "number" %}
        {# Type must be "text" or Firefox and Safari will set a blank value to the server if non numeric characters are entered -
        they don’t block non numeric characters: https://bugzilla.mozilla.org/show_bug.cgi?id=1398528 #}
        {% set type = "text" %}
        {% set pattern = "[0-9]*" %}
        {% set inputmode = "numeric" %}
    {% elif params.type is defined and params.type %}
        {% set type = params.type %}
    {% elif params.searchButton is defined and params.searchButton %}
        {% set type = "search" %}
    {% else %}
        {% set type = "text" %}
    {% endif %}

    {% set exclusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %}
    {% set inputPlaceholder = ' ons-input--placeholder' if params.accessiblePlaceholder else "" %}

    {% set input %}
        <input
            type="{{ type }}"
            id="{{ params.id }}"
            class="ons-input ons-input--text ons-input-type__input{% if params.error is defined and params.error %} ons-input--error{% endif %}{% if params.searchButton is defined and params.searchButton %} ons-search__input{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.width is defined and params.width %} ons-input{% if params.type is defined and (params.type === 'number' or params.type === 'tel') %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
            {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}title="{{ params.prefix.title if params.prefix }}{{ params.suffix.title if params.suffix }}"{% endif %}
            {% if params.name is defined and params.name %}name="{{ params.name }}"{% endif %}
            {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
            {% if params.accept is defined and params.accept %}accept="{{ params.accept }}"{% endif %}
            {% if params.min is defined and params.min %}min="{{ params.min }}"{% endif %}
            {% if params.max is defined and params.max %}max="{{ params.max }}"{% endif %}
            {% if params.minLength is defined and params.minLength %}minlength="{{ params.minLength }}"{% endif %}
            {% if params.maxLength is defined and params.maxLength %}maxlength="{{ params.maxLength }}"{% endif %}
            {% if pattern is defined and pattern %}pattern="{{ pattern }}"{% endif %}
            {% if inputmode is defined and inputmode %}inputmode="{{ inputmode }}"{% endif %}
            {% if params.autocomplete is defined and params.autocomplete %}autocomplete="{{ params.autocomplete }}"{% endif %}
            {% if params.accessiblePlaceholder is defined and params.accessiblePlaceholder %}placeholder="{{ params.label.text }}"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit %}data-char-check-ref="{{ params.id }}-check-remaining" data-char-check-num="{{ params.charCheckLimit.limit }}" aria-describedby="{{ params.id }}-check-remaining"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.charcheckCountdown is defined and params.charCheckLimit.charcheckCountdown %}data-char-check-countdown="true"{% 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 }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
            {% if params.label is defined and params.label and params.label.description is defined and params.label.description %}{% if params.label.id is defined and params.label.id %}aria-describedby="{{ params.label.id }}-description-hint"{% else %}aria-describedby="description-hint"{% endif %}{% endif %}
            {% if params.required is defined and params.required == true %}required="required"{% 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 %}
        {% if params.postTextboxLinkText is defined and params.postTextboxLinkText %}
            <a class="ons-u-fs-s" href="{{ params.postTextboxLinkUrl }}">{{ params.postTextboxLinkText }}</a>
        {% endif %}
    {% endset %}
    {% set field %}
        {% if params.label is defined and params.label and params.label.text is defined and params.label.text %}
            {{ onsLabel({
                "for": params.id,
                "id": params.label.id,
                "text": params.label.text,
                "classes": params.label.classes,
                "description": params.label.description,
                "attributes": params.label.attributes,
                "accessiblePlaceholder": params.accessiblePlaceholder
            }) }}
        {% endif %}

        {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}
            {% if params.prefix is defined and params.prefix %}
                {% set prefixClass = " ons-input-type--prefix" %}
            {% endif %}

            <span class="ons-input-type{{ prefixClass }}">
                <span class="ons-input-type__inner">
                    {{ input | safe }}
                    {% set abbr = params.prefix or params.suffix %}
                    <abbr
                        class="ons-input-type__type ons-js-input-abbr"
                        aria-hidden="true"
                        title="{{ abbr.title }}"
                        {% if abbr.id is defined and abbr.id %} id="{{ abbr.id }}"{% endif %}
                        >{{ abbr.text or abbr.title }}</abbr>
                </span>
            </span>
        {% elif params.searchButton is defined and params.searchButton %}
            <span class="ons-grid--flex ons-search">
                {% call onsSearch({
                    "accessiblePlaceholder": params.accessiblePlaceholder,
                    "searchButton": {
                        "type": params.searchButton.type,
                        "text": params.searchButton.text,
                        "id": params.searchButton.id,
                        "attributes": params.searchButton.attributes,
                        "classes": params.searchButton.classes
                    }
                }) %}
                    {{ input | safe }}
                {% endcall %}
            </span>
        {% else %}
            {{ input | safe }}
        {% endif %}
    {% endset %}

    {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
        {% set charCheckField %}
            {% call onsCharLimit({
                "id": params.id ~ "-check",
                "limit": params.charCheckLimit.limit,
                "type": "check",
                "charCountSingular": params.charCheckLimit.charCountSingular,
                "charCountPlural": params.charCheckLimit.charCountPlural,
                "charCountOverLimitSingular": params.charCheckLimit.charCountOverLimitSingular,
                "charCountOverLimitPlural": params.charCheckLimit.charCountOverLimitPlural
            }) %}
                {{ field | safe }}
            {% endcall %}
        {% endset %}
    {% endif %}

    {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
        {% call onsMutuallyExclusive({
            "id": params.fieldId,
            "legend": params.legend,
            "legendClasses": params.legendClasses ~ "ons-js-input-legend",
            "description": params.description,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "checkbox": params.mutuallyExclusive.checkbox,
            "or": params.mutuallyExclusive.or,
            "deselectMessage": params.mutuallyExclusive.deselectMessage,
            "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective,
            "deselectCheckboxAdjective": params.mutuallyExclusive.deselectCheckboxAdjective,
            "error": params.error,
            "autosuggestResults": params.autosuggestResults
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% elif type == "hidden" %}
        {{ field | safe }}
    {% else %}
        {% call onsField({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "error": params.error
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% endif %}
{% endmacro %}
.ons-input-type {
  display: block;

  // Keep the entire component display block, but use inline-flex on inner to prevent the orange focus from going full width
  &__inner {
    display: inline-flex;
    position: relative;
  }

  // Double ampersand is needed to solve specificity issues
  & &__input {
    flex: 1 1 auto;
    position: relative;
    z-index: 1;

    &:focus {
      box-shadow: inset 0 0 0 1px $color-input;
    }
  }

  &__type[title] {
    background-color: $color-button-secondary;
    border: 1px solid $color-input;
    display: block;
    flex: 0 0 auto;
    font-size: 1rem;
    font-weight: $font-weight-bold;
    line-height: normal;
    padding: $input-padding-vertical $input-padding-horizontal * 2;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
  }

  &__input:focus + &__type[title]::after {
    border-radius: $input-radius;
    bottom: 0;
    box-shadow: 0 0 0 3px $color-focus;
    content: '';
    display: block;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }

  &:not(&--prefix) & {
    &__type[title] {
      border-left: 0;
      border-radius: 0 $input-radius $input-radius 0;
    }

    &__input {
      border-radius: $input-radius 0 0 $input-radius;
    }
  }

  &--prefix & {
    &__type[title] {
      border-radius: $input-radius 0 0 $input-radius;
      border-right: 0;
      order: 0;
    }

    &__input {
      border-radius: 0 $input-radius $input-radius 0;
      order: 1;
    }
  }
}

.ons-input {
  border: $input-border-width solid $color-input;
  border-radius: $input-radius;
  color: inherit;
  display: block;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1rem;
  padding: $input-padding-vertical $input-padding-horizontal;
  position: relative;
  width: 100%;
  z-index: 3;

  &::-ms-clear {
    display: none;
  }

  @include mq(s) {
    &--text,
    &--select {
      &:not(.ons-input--block):not([class*='input--w-']) {
        width: $input-width;
      }
    }
  }

  &--text,
  &--textarea {
    // Prevent inner shadow on iOS
    appearance: none;
  }

  &:focus {
    box-shadow: 0 0 0 3px $color-focus, inset 0 0 0 1px $color-input;
    outline: none;
  }

  &:disabled {
    border-color: $color-grey-75;
    cursor: not-allowed;
  }

  &--error:not(:focus) {
    border: 1px solid $color-ruby-red;
    box-shadow: inset 0 0 0 1px $color-ruby-red;
  }

  &--with-description {
    margin-bottom: 0.55rem;
  }
}

// Text input widths
@include input-width('ons-input--w-{x}');

// Number input widths
@include input-width('ons-input-number--w-{x}', 0.54rem);

.ons-input--postcode {
  max-width: input-width-calc($chars: 5, $num-chars: 2, $spaces: 1);
  width: 100%;
}

.ons-input__helper {
  font-size: 0.8rem;
  font-weight: $font-weight-bold;
  margin-top: 0.2rem;
}

.ons-input--select {
  appearance: none;
  background: $color-white url('#{$static}/img/icons--chevron-down.svg') no-repeat center right 10px;
  background-size: 1rem;
  line-height: 1.3rem;
  padding: 0.39rem 2rem 0.39rem $input-padding-horizontal;

  &::-ms-expand {
    display: none;
  }
}

.ons-input--textarea {
  line-height: normal;
  resize: vertical;
  width: 100%;
}

.ons-input--block {
  display: block;
  width: 100%;
}

.ons-input--placeholder {
  background: transparent;
  &::placeholder {
    color: transparent;
  }
  &:valid:not(:placeholder-shown) {
    background-color: $color-white;
  }
  &:focus {
    background-color: $color-white;
  }
}

.ons-input--limit-reached:not(:focus) {
  border: $input-border-width solid $color-ruby-red;
}

.ons-input__limit {
  display: block;

  &--reached {
    color: $color-ruby-red;
  }
}

.ons-input--ghost {
  border: 2px solid rgba(255, 255, 255, 0.6);
  &:focus {
    border: 2px solid $color-input;
  }
}

.ons-input-search {
  @extend .ons-input--block;

  &--icon {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    background-position: 9px 7px;
    background-repeat: no-repeat;
    background-size: 24px 24px;
    padding-left: 2.4rem;

    &:focus,
    &:active,
    &:valid:not(:placeholder-shown) {
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 9px 7px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

There is specific guidance on how to ask users for email addresses.

Telephone

Setting the type to tel will bring up the correct keypad on touch devices.

<div class="ons-field">
  <label class="ons-label  ons-label--with-description " for="telephone">UK mobile number
  </label>
  <span id="description-hint" class="ons-label__description  ons-input--with-description">
    This will not be stored and only used once to send your access code
  </span>
  <input type="tel" id="telephone" class="ons-input ons-input--text ons-input-type__input ons-input-number--w-15" autocomplete="tel" aria-describedby="description-hint" />
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "telephone",
        "type": "tel",
        "autocomplete": "tel",
        "width": "15",
        "label": {
            "text": "UK mobile number",
            "description": "This will not be stored and only used once to send your access code"
        }
    })
}}
Name Type Required Description
id string true The id of the input. This will also be added to the label if a label is specified
type string false The type of the input, for example, number, email, tel. Will default to text
classes string false Classes to add to the input.
width string false Required width of the input. Will also accept the bp-suffix e.g. '7@m'
name string false The name of the input
value string | number false The value to set the input to
min number false Minimum accepted number or date
max number false Maximum accepted number or date
minLength number false Minimum accepted length of input value
maxLength number false Maximum accepted length of input value
attributes object false HTML attributes (for example, data attributes) to add to the input
label Label (ref) false Settings for the input label. for will automatically be set to match the input id
prefix InputPrefix false Settings to prefix the input with
suffix InputSuffix false Settings to suffix the input with
fieldId string false Id for the field
fieldClasses string false Classes for the field
dontWrap boolean false Prevents the input from being wrapped in a field component
mutuallyExclusive MutuallyExclusive (ref) false Configuration object if this is a mutually exclusive input
CharCheckLimit CharCheckLimit false Configuration object if this input has a character count
legend string Only if mutuallyExclusive is set Text content for the legend
legendClasses string false Classes for the legend
error Error (ref) false Configuration for validation errors
autocomplete string true Autocomplete attribute used to override the browsers native autocomplete
accessiblePlaceholder boolean false Will add the provided label as an accessible placeholder
searchButton Button (ref) false Settings for the button used for a search pattern.
postTextboxLinkText string false The text for the link to follow the textbox
postTextboxLinkUrl string false The url for the link to follow the textbox
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
required boolean false Adds the required attribute to the input to indicate that the user must specify a value for the input before the owning form can be submitted

Prefix/Suffix

Name Type Required Description
text string true The text for the prefix/suffix
title string true The title of the prefix/suffix. For example where text is “cm”, title would be “centimetres”
id string false Id for the prefix/suffix

CharCheckLimit

Name Type Required Description
limit number false The maximum amount of characters a user should type in
charcheckCountdown boolean false Displays the number of remaining characters allowed based on the limit
charCountPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining. {x} Will be replaced with the number
charCountSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining (singular). {x} Will be replaced with the number
charCountOverLimitSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (singular). {x} Will be replaced with the number
charCountOverLimitPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (plural). {x} Will be replaced with the number
{% macro onsInput(params) %}
    {% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %}
    {% from "components/char-check-limit/_macro.njk" import onsCharLimit %}
    {% from "components/search/_macro.njk" import onsSearch %}
    {% from "components/field/_macro.njk" import onsField %}
    {% from "components/label/_macro.njk" import onsLabel %}

    {% if params.type == "number" %}
        {# Type must be "text" or Firefox and Safari will set a blank value to the server if non numeric characters are entered -
        they don’t block non numeric characters: https://bugzilla.mozilla.org/show_bug.cgi?id=1398528 #}
        {% set type = "text" %}
        {% set pattern = "[0-9]*" %}
        {% set inputmode = "numeric" %}
    {% elif params.type is defined and params.type %}
        {% set type = params.type %}
    {% elif params.searchButton is defined and params.searchButton %}
        {% set type = "search" %}
    {% else %}
        {% set type = "text" %}
    {% endif %}

    {% set exclusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %}
    {% set inputPlaceholder = ' ons-input--placeholder' if params.accessiblePlaceholder else "" %}

    {% set input %}
        <input
            type="{{ type }}"
            id="{{ params.id }}"
            class="ons-input ons-input--text ons-input-type__input{% if params.error is defined and params.error %} ons-input--error{% endif %}{% if params.searchButton is defined and params.searchButton %} ons-search__input{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.width is defined and params.width %} ons-input{% if params.type is defined and (params.type === 'number' or params.type === 'tel') %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
            {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}title="{{ params.prefix.title if params.prefix }}{{ params.suffix.title if params.suffix }}"{% endif %}
            {% if params.name is defined and params.name %}name="{{ params.name }}"{% endif %}
            {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
            {% if params.accept is defined and params.accept %}accept="{{ params.accept }}"{% endif %}
            {% if params.min is defined and params.min %}min="{{ params.min }}"{% endif %}
            {% if params.max is defined and params.max %}max="{{ params.max }}"{% endif %}
            {% if params.minLength is defined and params.minLength %}minlength="{{ params.minLength }}"{% endif %}
            {% if params.maxLength is defined and params.maxLength %}maxlength="{{ params.maxLength }}"{% endif %}
            {% if pattern is defined and pattern %}pattern="{{ pattern }}"{% endif %}
            {% if inputmode is defined and inputmode %}inputmode="{{ inputmode }}"{% endif %}
            {% if params.autocomplete is defined and params.autocomplete %}autocomplete="{{ params.autocomplete }}"{% endif %}
            {% if params.accessiblePlaceholder is defined and params.accessiblePlaceholder %}placeholder="{{ params.label.text }}"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit %}data-char-check-ref="{{ params.id }}-check-remaining" data-char-check-num="{{ params.charCheckLimit.limit }}" aria-describedby="{{ params.id }}-check-remaining"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.charcheckCountdown is defined and params.charCheckLimit.charcheckCountdown %}data-char-check-countdown="true"{% 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 }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
            {% if params.label is defined and params.label and params.label.description is defined and params.label.description %}{% if params.label.id is defined and params.label.id %}aria-describedby="{{ params.label.id }}-description-hint"{% else %}aria-describedby="description-hint"{% endif %}{% endif %}
            {% if params.required is defined and params.required == true %}required="required"{% 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 %}
        {% if params.postTextboxLinkText is defined and params.postTextboxLinkText %}
            <a class="ons-u-fs-s" href="{{ params.postTextboxLinkUrl }}">{{ params.postTextboxLinkText }}</a>
        {% endif %}
    {% endset %}
    {% set field %}
        {% if params.label is defined and params.label and params.label.text is defined and params.label.text %}
            {{ onsLabel({
                "for": params.id,
                "id": params.label.id,
                "text": params.label.text,
                "classes": params.label.classes,
                "description": params.label.description,
                "attributes": params.label.attributes,
                "accessiblePlaceholder": params.accessiblePlaceholder
            }) }}
        {% endif %}

        {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}
            {% if params.prefix is defined and params.prefix %}
                {% set prefixClass = " ons-input-type--prefix" %}
            {% endif %}

            <span class="ons-input-type{{ prefixClass }}">
                <span class="ons-input-type__inner">
                    {{ input | safe }}
                    {% set abbr = params.prefix or params.suffix %}
                    <abbr
                        class="ons-input-type__type ons-js-input-abbr"
                        aria-hidden="true"
                        title="{{ abbr.title }}"
                        {% if abbr.id is defined and abbr.id %} id="{{ abbr.id }}"{% endif %}
                        >{{ abbr.text or abbr.title }}</abbr>
                </span>
            </span>
        {% elif params.searchButton is defined and params.searchButton %}
            <span class="ons-grid--flex ons-search">
                {% call onsSearch({
                    "accessiblePlaceholder": params.accessiblePlaceholder,
                    "searchButton": {
                        "type": params.searchButton.type,
                        "text": params.searchButton.text,
                        "id": params.searchButton.id,
                        "attributes": params.searchButton.attributes,
                        "classes": params.searchButton.classes
                    }
                }) %}
                    {{ input | safe }}
                {% endcall %}
            </span>
        {% else %}
            {{ input | safe }}
        {% endif %}
    {% endset %}

    {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
        {% set charCheckField %}
            {% call onsCharLimit({
                "id": params.id ~ "-check",
                "limit": params.charCheckLimit.limit,
                "type": "check",
                "charCountSingular": params.charCheckLimit.charCountSingular,
                "charCountPlural": params.charCheckLimit.charCountPlural,
                "charCountOverLimitSingular": params.charCheckLimit.charCountOverLimitSingular,
                "charCountOverLimitPlural": params.charCheckLimit.charCountOverLimitPlural
            }) %}
                {{ field | safe }}
            {% endcall %}
        {% endset %}
    {% endif %}

    {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
        {% call onsMutuallyExclusive({
            "id": params.fieldId,
            "legend": params.legend,
            "legendClasses": params.legendClasses ~ "ons-js-input-legend",
            "description": params.description,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "checkbox": params.mutuallyExclusive.checkbox,
            "or": params.mutuallyExclusive.or,
            "deselectMessage": params.mutuallyExclusive.deselectMessage,
            "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective,
            "deselectCheckboxAdjective": params.mutuallyExclusive.deselectCheckboxAdjective,
            "error": params.error,
            "autosuggestResults": params.autosuggestResults
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% elif type == "hidden" %}
        {{ field | safe }}
    {% else %}
        {% call onsField({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "error": params.error
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% endif %}
{% endmacro %}
.ons-input-type {
  display: block;

  // Keep the entire component display block, but use inline-flex on inner to prevent the orange focus from going full width
  &__inner {
    display: inline-flex;
    position: relative;
  }

  // Double ampersand is needed to solve specificity issues
  & &__input {
    flex: 1 1 auto;
    position: relative;
    z-index: 1;

    &:focus {
      box-shadow: inset 0 0 0 1px $color-input;
    }
  }

  &__type[title] {
    background-color: $color-button-secondary;
    border: 1px solid $color-input;
    display: block;
    flex: 0 0 auto;
    font-size: 1rem;
    font-weight: $font-weight-bold;
    line-height: normal;
    padding: $input-padding-vertical $input-padding-horizontal * 2;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
  }

  &__input:focus + &__type[title]::after {
    border-radius: $input-radius;
    bottom: 0;
    box-shadow: 0 0 0 3px $color-focus;
    content: '';
    display: block;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }

  &:not(&--prefix) & {
    &__type[title] {
      border-left: 0;
      border-radius: 0 $input-radius $input-radius 0;
    }

    &__input {
      border-radius: $input-radius 0 0 $input-radius;
    }
  }

  &--prefix & {
    &__type[title] {
      border-radius: $input-radius 0 0 $input-radius;
      border-right: 0;
      order: 0;
    }

    &__input {
      border-radius: 0 $input-radius $input-radius 0;
      order: 1;
    }
  }
}

.ons-input {
  border: $input-border-width solid $color-input;
  border-radius: $input-radius;
  color: inherit;
  display: block;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1rem;
  padding: $input-padding-vertical $input-padding-horizontal;
  position: relative;
  width: 100%;
  z-index: 3;

  &::-ms-clear {
    display: none;
  }

  @include mq(s) {
    &--text,
    &--select {
      &:not(.ons-input--block):not([class*='input--w-']) {
        width: $input-width;
      }
    }
  }

  &--text,
  &--textarea {
    // Prevent inner shadow on iOS
    appearance: none;
  }

  &:focus {
    box-shadow: 0 0 0 3px $color-focus, inset 0 0 0 1px $color-input;
    outline: none;
  }

  &:disabled {
    border-color: $color-grey-75;
    cursor: not-allowed;
  }

  &--error:not(:focus) {
    border: 1px solid $color-ruby-red;
    box-shadow: inset 0 0 0 1px $color-ruby-red;
  }

  &--with-description {
    margin-bottom: 0.55rem;
  }
}

// Text input widths
@include input-width('ons-input--w-{x}');

// Number input widths
@include input-width('ons-input-number--w-{x}', 0.54rem);

.ons-input--postcode {
  max-width: input-width-calc($chars: 5, $num-chars: 2, $spaces: 1);
  width: 100%;
}

.ons-input__helper {
  font-size: 0.8rem;
  font-weight: $font-weight-bold;
  margin-top: 0.2rem;
}

.ons-input--select {
  appearance: none;
  background: $color-white url('#{$static}/img/icons--chevron-down.svg') no-repeat center right 10px;
  background-size: 1rem;
  line-height: 1.3rem;
  padding: 0.39rem 2rem 0.39rem $input-padding-horizontal;

  &::-ms-expand {
    display: none;
  }
}

.ons-input--textarea {
  line-height: normal;
  resize: vertical;
  width: 100%;
}

.ons-input--block {
  display: block;
  width: 100%;
}

.ons-input--placeholder {
  background: transparent;
  &::placeholder {
    color: transparent;
  }
  &:valid:not(:placeholder-shown) {
    background-color: $color-white;
  }
  &:focus {
    background-color: $color-white;
  }
}

.ons-input--limit-reached:not(:focus) {
  border: $input-border-width solid $color-ruby-red;
}

.ons-input__limit {
  display: block;

  &--reached {
    color: $color-ruby-red;
  }
}

.ons-input--ghost {
  border: 2px solid rgba(255, 255, 255, 0.6);
  &:focus {
    border: 2px solid $color-input;
  }
}

.ons-input-search {
  @extend .ons-input--block;

  &--icon {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    background-position: 9px 7px;
    background-repeat: no-repeat;
    background-size: 24px 24px;
    padding-left: 2.4rem;

    &:focus,
    &:active,
    &:valid:not(:placeholder-shown) {
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 9px 7px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

There is specific guidance on how to ask users for phone numbers.

Number

Setting the type to number will bring up the correct keypad on touch devices.

<div class="ons-field">
  <label class="ons-label  " for="prefixed">Your annual salary before taxes
  </label>
  <span class="ons-input-type ons-input-type--prefix">
    <span class="ons-input-type__inner">
      <input type="text" id="prefixed" class="ons-input ons-input--text ons-input-type__input ons-input-number--w-6" title="British pounds (GBP)" pattern="[0-9]*" inputmode="numeric" autocomplete="off" required="required" />
      <abbr class="ons-input-type__type ons-js-input-abbr" aria-hidden="true" title="British pounds (GBP)">£</abbr>
    </span>
  </span>
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "prefixed",
        "type": "number",
        "width": "6",
        "autocomplete": "off",
        "required": true,
        "label": {
            "text": "Your annual salary before taxes"
        },
        "prefix": {
            "title": "British pounds (GBP)",
            "text": "£"
        }
    })
}}
Name Type Required Description
id string true The id of the input. This will also be added to the label if a label is specified
type string false The type of the input, for example, number, email, tel. Will default to text
classes string false Classes to add to the input.
width string false Required width of the input. Will also accept the bp-suffix e.g. '7@m'
name string false The name of the input
value string | number false The value to set the input to
min number false Minimum accepted number or date
max number false Maximum accepted number or date
minLength number false Minimum accepted length of input value
maxLength number false Maximum accepted length of input value
attributes object false HTML attributes (for example, data attributes) to add to the input
label Label (ref) false Settings for the input label. for will automatically be set to match the input id
prefix InputPrefix false Settings to prefix the input with
suffix InputSuffix false Settings to suffix the input with
fieldId string false Id for the field
fieldClasses string false Classes for the field
dontWrap boolean false Prevents the input from being wrapped in a field component
mutuallyExclusive MutuallyExclusive (ref) false Configuration object if this is a mutually exclusive input
CharCheckLimit CharCheckLimit false Configuration object if this input has a character count
legend string Only if mutuallyExclusive is set Text content for the legend
legendClasses string false Classes for the legend
error Error (ref) false Configuration for validation errors
autocomplete string true Autocomplete attribute used to override the browsers native autocomplete
accessiblePlaceholder boolean false Will add the provided label as an accessible placeholder
searchButton Button (ref) false Settings for the button used for a search pattern.
postTextboxLinkText string false The text for the link to follow the textbox
postTextboxLinkUrl string false The url for the link to follow the textbox
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
required boolean false Adds the required attribute to the input to indicate that the user must specify a value for the input before the owning form can be submitted

Prefix/Suffix

Name Type Required Description
text string true The text for the prefix/suffix
title string true The title of the prefix/suffix. For example where text is “cm”, title would be “centimetres”
id string false Id for the prefix/suffix

CharCheckLimit

Name Type Required Description
limit number false The maximum amount of characters a user should type in
charcheckCountdown boolean false Displays the number of remaining characters allowed based on the limit
charCountPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining. {x} Will be replaced with the number
charCountSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining (singular). {x} Will be replaced with the number
charCountOverLimitSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (singular). {x} Will be replaced with the number
charCountOverLimitPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (plural). {x} Will be replaced with the number
{% macro onsInput(params) %}
    {% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %}
    {% from "components/char-check-limit/_macro.njk" import onsCharLimit %}
    {% from "components/search/_macro.njk" import onsSearch %}
    {% from "components/field/_macro.njk" import onsField %}
    {% from "components/label/_macro.njk" import onsLabel %}

    {% if params.type == "number" %}
        {# Type must be "text" or Firefox and Safari will set a blank value to the server if non numeric characters are entered -
        they don’t block non numeric characters: https://bugzilla.mozilla.org/show_bug.cgi?id=1398528 #}
        {% set type = "text" %}
        {% set pattern = "[0-9]*" %}
        {% set inputmode = "numeric" %}
    {% elif params.type is defined and params.type %}
        {% set type = params.type %}
    {% elif params.searchButton is defined and params.searchButton %}
        {% set type = "search" %}
    {% else %}
        {% set type = "text" %}
    {% endif %}

    {% set exclusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %}
    {% set inputPlaceholder = ' ons-input--placeholder' if params.accessiblePlaceholder else "" %}

    {% set input %}
        <input
            type="{{ type }}"
            id="{{ params.id }}"
            class="ons-input ons-input--text ons-input-type__input{% if params.error is defined and params.error %} ons-input--error{% endif %}{% if params.searchButton is defined and params.searchButton %} ons-search__input{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.width is defined and params.width %} ons-input{% if params.type is defined and (params.type === 'number' or params.type === 'tel') %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
            {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}title="{{ params.prefix.title if params.prefix }}{{ params.suffix.title if params.suffix }}"{% endif %}
            {% if params.name is defined and params.name %}name="{{ params.name }}"{% endif %}
            {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
            {% if params.accept is defined and params.accept %}accept="{{ params.accept }}"{% endif %}
            {% if params.min is defined and params.min %}min="{{ params.min }}"{% endif %}
            {% if params.max is defined and params.max %}max="{{ params.max }}"{% endif %}
            {% if params.minLength is defined and params.minLength %}minlength="{{ params.minLength }}"{% endif %}
            {% if params.maxLength is defined and params.maxLength %}maxlength="{{ params.maxLength }}"{% endif %}
            {% if pattern is defined and pattern %}pattern="{{ pattern }}"{% endif %}
            {% if inputmode is defined and inputmode %}inputmode="{{ inputmode }}"{% endif %}
            {% if params.autocomplete is defined and params.autocomplete %}autocomplete="{{ params.autocomplete }}"{% endif %}
            {% if params.accessiblePlaceholder is defined and params.accessiblePlaceholder %}placeholder="{{ params.label.text }}"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit %}data-char-check-ref="{{ params.id }}-check-remaining" data-char-check-num="{{ params.charCheckLimit.limit }}" aria-describedby="{{ params.id }}-check-remaining"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.charcheckCountdown is defined and params.charCheckLimit.charcheckCountdown %}data-char-check-countdown="true"{% 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 }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
            {% if params.label is defined and params.label and params.label.description is defined and params.label.description %}{% if params.label.id is defined and params.label.id %}aria-describedby="{{ params.label.id }}-description-hint"{% else %}aria-describedby="description-hint"{% endif %}{% endif %}
            {% if params.required is defined and params.required == true %}required="required"{% 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 %}
        {% if params.postTextboxLinkText is defined and params.postTextboxLinkText %}
            <a class="ons-u-fs-s" href="{{ params.postTextboxLinkUrl }}">{{ params.postTextboxLinkText }}</a>
        {% endif %}
    {% endset %}
    {% set field %}
        {% if params.label is defined and params.label and params.label.text is defined and params.label.text %}
            {{ onsLabel({
                "for": params.id,
                "id": params.label.id,
                "text": params.label.text,
                "classes": params.label.classes,
                "description": params.label.description,
                "attributes": params.label.attributes,
                "accessiblePlaceholder": params.accessiblePlaceholder
            }) }}
        {% endif %}

        {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}
            {% if params.prefix is defined and params.prefix %}
                {% set prefixClass = " ons-input-type--prefix" %}
            {% endif %}

            <span class="ons-input-type{{ prefixClass }}">
                <span class="ons-input-type__inner">
                    {{ input | safe }}
                    {% set abbr = params.prefix or params.suffix %}
                    <abbr
                        class="ons-input-type__type ons-js-input-abbr"
                        aria-hidden="true"
                        title="{{ abbr.title }}"
                        {% if abbr.id is defined and abbr.id %} id="{{ abbr.id }}"{% endif %}
                        >{{ abbr.text or abbr.title }}</abbr>
                </span>
            </span>
        {% elif params.searchButton is defined and params.searchButton %}
            <span class="ons-grid--flex ons-search">
                {% call onsSearch({
                    "accessiblePlaceholder": params.accessiblePlaceholder,
                    "searchButton": {
                        "type": params.searchButton.type,
                        "text": params.searchButton.text,
                        "id": params.searchButton.id,
                        "attributes": params.searchButton.attributes,
                        "classes": params.searchButton.classes
                    }
                }) %}
                    {{ input | safe }}
                {% endcall %}
            </span>
        {% else %}
            {{ input | safe }}
        {% endif %}
    {% endset %}

    {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
        {% set charCheckField %}
            {% call onsCharLimit({
                "id": params.id ~ "-check",
                "limit": params.charCheckLimit.limit,
                "type": "check",
                "charCountSingular": params.charCheckLimit.charCountSingular,
                "charCountPlural": params.charCheckLimit.charCountPlural,
                "charCountOverLimitSingular": params.charCheckLimit.charCountOverLimitSingular,
                "charCountOverLimitPlural": params.charCheckLimit.charCountOverLimitPlural
            }) %}
                {{ field | safe }}
            {% endcall %}
        {% endset %}
    {% endif %}

    {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
        {% call onsMutuallyExclusive({
            "id": params.fieldId,
            "legend": params.legend,
            "legendClasses": params.legendClasses ~ "ons-js-input-legend",
            "description": params.description,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "checkbox": params.mutuallyExclusive.checkbox,
            "or": params.mutuallyExclusive.or,
            "deselectMessage": params.mutuallyExclusive.deselectMessage,
            "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective,
            "deselectCheckboxAdjective": params.mutuallyExclusive.deselectCheckboxAdjective,
            "error": params.error,
            "autosuggestResults": params.autosuggestResults
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% elif type == "hidden" %}
        {{ field | safe }}
    {% else %}
        {% call onsField({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "error": params.error
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% endif %}
{% endmacro %}
.ons-input-type {
  display: block;

  // Keep the entire component display block, but use inline-flex on inner to prevent the orange focus from going full width
  &__inner {
    display: inline-flex;
    position: relative;
  }

  // Double ampersand is needed to solve specificity issues
  & &__input {
    flex: 1 1 auto;
    position: relative;
    z-index: 1;

    &:focus {
      box-shadow: inset 0 0 0 1px $color-input;
    }
  }

  &__type[title] {
    background-color: $color-button-secondary;
    border: 1px solid $color-input;
    display: block;
    flex: 0 0 auto;
    font-size: 1rem;
    font-weight: $font-weight-bold;
    line-height: normal;
    padding: $input-padding-vertical $input-padding-horizontal * 2;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
  }

  &__input:focus + &__type[title]::after {
    border-radius: $input-radius;
    bottom: 0;
    box-shadow: 0 0 0 3px $color-focus;
    content: '';
    display: block;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }

  &:not(&--prefix) & {
    &__type[title] {
      border-left: 0;
      border-radius: 0 $input-radius $input-radius 0;
    }

    &__input {
      border-radius: $input-radius 0 0 $input-radius;
    }
  }

  &--prefix & {
    &__type[title] {
      border-radius: $input-radius 0 0 $input-radius;
      border-right: 0;
      order: 0;
    }

    &__input {
      border-radius: 0 $input-radius $input-radius 0;
      order: 1;
    }
  }
}

.ons-input {
  border: $input-border-width solid $color-input;
  border-radius: $input-radius;
  color: inherit;
  display: block;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1rem;
  padding: $input-padding-vertical $input-padding-horizontal;
  position: relative;
  width: 100%;
  z-index: 3;

  &::-ms-clear {
    display: none;
  }

  @include mq(s) {
    &--text,
    &--select {
      &:not(.ons-input--block):not([class*='input--w-']) {
        width: $input-width;
      }
    }
  }

  &--text,
  &--textarea {
    // Prevent inner shadow on iOS
    appearance: none;
  }

  &:focus {
    box-shadow: 0 0 0 3px $color-focus, inset 0 0 0 1px $color-input;
    outline: none;
  }

  &:disabled {
    border-color: $color-grey-75;
    cursor: not-allowed;
  }

  &--error:not(:focus) {
    border: 1px solid $color-ruby-red;
    box-shadow: inset 0 0 0 1px $color-ruby-red;
  }

  &--with-description {
    margin-bottom: 0.55rem;
  }
}

// Text input widths
@include input-width('ons-input--w-{x}');

// Number input widths
@include input-width('ons-input-number--w-{x}', 0.54rem);

.ons-input--postcode {
  max-width: input-width-calc($chars: 5, $num-chars: 2, $spaces: 1);
  width: 100%;
}

.ons-input__helper {
  font-size: 0.8rem;
  font-weight: $font-weight-bold;
  margin-top: 0.2rem;
}

.ons-input--select {
  appearance: none;
  background: $color-white url('#{$static}/img/icons--chevron-down.svg') no-repeat center right 10px;
  background-size: 1rem;
  line-height: 1.3rem;
  padding: 0.39rem 2rem 0.39rem $input-padding-horizontal;

  &::-ms-expand {
    display: none;
  }
}

.ons-input--textarea {
  line-height: normal;
  resize: vertical;
  width: 100%;
}

.ons-input--block {
  display: block;
  width: 100%;
}

.ons-input--placeholder {
  background: transparent;
  &::placeholder {
    color: transparent;
  }
  &:valid:not(:placeholder-shown) {
    background-color: $color-white;
  }
  &:focus {
    background-color: $color-white;
  }
}

.ons-input--limit-reached:not(:focus) {
  border: $input-border-width solid $color-ruby-red;
}

.ons-input__limit {
  display: block;

  &--reached {
    color: $color-ruby-red;
  }
}

.ons-input--ghost {
  border: 2px solid rgba(255, 255, 255, 0.6);
  &:focus {
    border: 2px solid $color-input;
  }
}

.ons-input-search {
  @extend .ons-input--block;

  &--icon {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    background-position: 9px 7px;
    background-repeat: no-repeat;
    background-size: 24px 24px;
    padding-left: 2.4rem;

    &:focus,
    &:active,
    &:valid:not(:placeholder-shown) {
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 9px 7px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

There is specific guidance on how to ask users for numeric values including entering numbers with units, such as currencies and measurements.

Character limit checker

Use this for client-side validation of an input that needs a character limit.

Use this variant as little as possible. Users do not expect a limit on inputs, and often do not look up from a keyboard as they type. This means they may not notice the counter.

The character counter is displayed when the charCheckLimit object is defined with the limit, charCountOverLimitSingular and charCountOverLimitPlural parameters.

<div class="ons-field">
  <span class="ons-js-char-check-input">
    <label class="ons-label  " for="text">Enter some text
    </label>
    <input type="text" id="text" class="ons-input ons-input--text ons-input-type__input ons-input--w-10" data-char-check-ref="text-check-remaining" data-char-check-num="10" aria-describedby="text-check-remaining" />
  </span>
  <span id="text-check-remaining" class="ons-input__limit ons-u-fs-s--b ons-u-d-no ons-u-mt-xs" data-charcount-singular="You have {x} character remaining" data-charcount-plural="You have {x} characters remaining" data-charcount-limit-singular="{x} character too many" data-charcount-limit-plural="{x} characters too many">
  </span>
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "text",
        "label": {
            "text": "Enter some text"
        },
        "width": "10",
        "charCheckLimit": {
            "limit": 10,
            "charCountOverLimitSingular": "{x} character too many",
            "charCountOverLimitPlural": "{x} characters too many",
            "charCountSingular": "You have {x} character remaining",
            "charCountPlural": "You have {x} characters remaining"
        }
    })
}}
Name Type Required Description
id string true The id of the input. This will also be added to the label if a label is specified
type string false The type of the input, for example, number, email, tel. Will default to text
classes string false Classes to add to the input.
width string false Required width of the input. Will also accept the bp-suffix e.g. '7@m'
name string false The name of the input
value string | number false The value to set the input to
min number false Minimum accepted number or date
max number false Maximum accepted number or date
minLength number false Minimum accepted length of input value
maxLength number false Maximum accepted length of input value
attributes object false HTML attributes (for example, data attributes) to add to the input
label Label (ref) false Settings for the input label. for will automatically be set to match the input id
prefix InputPrefix false Settings to prefix the input with
suffix InputSuffix false Settings to suffix the input with
fieldId string false Id for the field
fieldClasses string false Classes for the field
dontWrap boolean false Prevents the input from being wrapped in a field component
mutuallyExclusive MutuallyExclusive (ref) false Configuration object if this is a mutually exclusive input
CharCheckLimit CharCheckLimit false Configuration object if this input has a character count
legend string Only if mutuallyExclusive is set Text content for the legend
legendClasses string false Classes for the legend
error Error (ref) false Configuration for validation errors
autocomplete string true Autocomplete attribute used to override the browsers native autocomplete
accessiblePlaceholder boolean false Will add the provided label as an accessible placeholder
searchButton Button (ref) false Settings for the button used for a search pattern.
postTextboxLinkText string false The text for the link to follow the textbox
postTextboxLinkUrl string false The url for the link to follow the textbox
listeners object false Creates a script element that adds an event listener to the element by id. Takes key { event } and value { function }
required boolean false Adds the required attribute to the input to indicate that the user must specify a value for the input before the owning form can be submitted

Prefix/Suffix

Name Type Required Description
text string true The text for the prefix/suffix
title string true The title of the prefix/suffix. For example where text is “cm”, title would be “centimetres”
id string false Id for the prefix/suffix

CharCheckLimit

Name Type Required Description
limit number false The maximum amount of characters a user should type in
charcheckCountdown boolean false Displays the number of remaining characters allowed based on the limit
charCountPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining. {x} Will be replaced with the number
charCountSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining (singular). {x} Will be replaced with the number
charCountOverLimitSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (singular). {x} Will be replaced with the number
charCountOverLimitPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are over (plural). {x} Will be replaced with the number
{% macro onsInput(params) %}
    {% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %}
    {% from "components/char-check-limit/_macro.njk" import onsCharLimit %}
    {% from "components/search/_macro.njk" import onsSearch %}
    {% from "components/field/_macro.njk" import onsField %}
    {% from "components/label/_macro.njk" import onsLabel %}

    {% if params.type == "number" %}
        {# Type must be "text" or Firefox and Safari will set a blank value to the server if non numeric characters are entered -
        they don’t block non numeric characters: https://bugzilla.mozilla.org/show_bug.cgi?id=1398528 #}
        {% set type = "text" %}
        {% set pattern = "[0-9]*" %}
        {% set inputmode = "numeric" %}
    {% elif params.type is defined and params.type %}
        {% set type = params.type %}
    {% elif params.searchButton is defined and params.searchButton %}
        {% set type = "search" %}
    {% else %}
        {% set type = "text" %}
    {% endif %}

    {% set exclusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %}
    {% set inputPlaceholder = ' ons-input--placeholder' if params.accessiblePlaceholder else "" %}

    {% set input %}
        <input
            type="{{ type }}"
            id="{{ params.id }}"
            class="ons-input ons-input--text ons-input-type__input{% if params.error is defined and params.error %} ons-input--error{% endif %}{% if params.searchButton is defined and params.searchButton %} ons-search__input{% endif %}{% if params.classes is defined and params.classes %} {{ params.classes }}{% endif %}{% if params.width is defined and params.width %} ons-input{% if params.type is defined and (params.type === 'number' or params.type === 'tel') %}-number{% endif %}--w-{{ params.width }}{% endif %}{{ exclusiveClass }}{{ inputPlaceholder }}"
            {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}title="{{ params.prefix.title if params.prefix }}{{ params.suffix.title if params.suffix }}"{% endif %}
            {% if params.name is defined and params.name %}name="{{ params.name }}"{% endif %}
            {% if params.value is defined and params.value %}value="{{ params.value }}"{% endif %}
            {% if params.accept is defined and params.accept %}accept="{{ params.accept }}"{% endif %}
            {% if params.min is defined and params.min %}min="{{ params.min }}"{% endif %}
            {% if params.max is defined and params.max %}max="{{ params.max }}"{% endif %}
            {% if params.minLength is defined and params.minLength %}minlength="{{ params.minLength }}"{% endif %}
            {% if params.maxLength is defined and params.maxLength %}maxlength="{{ params.maxLength }}"{% endif %}
            {% if pattern is defined and pattern %}pattern="{{ pattern }}"{% endif %}
            {% if inputmode is defined and inputmode %}inputmode="{{ inputmode }}"{% endif %}
            {% if params.autocomplete is defined and params.autocomplete %}autocomplete="{{ params.autocomplete }}"{% endif %}
            {% if params.accessiblePlaceholder is defined and params.accessiblePlaceholder %}placeholder="{{ params.label.text }}"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit %}data-char-check-ref="{{ params.id }}-check-remaining" data-char-check-num="{{ params.charCheckLimit.limit }}" aria-describedby="{{ params.id }}-check-remaining"{% endif %}
            {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.charcheckCountdown is defined and params.charCheckLimit.charcheckCountdown %}data-char-check-countdown="true"{% 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 }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
            {% if params.label is defined and params.label and params.label.description is defined and params.label.description %}{% if params.label.id is defined and params.label.id %}aria-describedby="{{ params.label.id }}-description-hint"{% else %}aria-describedby="description-hint"{% endif %}{% endif %}
            {% if params.required is defined and params.required == true %}required="required"{% 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 %}
        {% if params.postTextboxLinkText is defined and params.postTextboxLinkText %}
            <a class="ons-u-fs-s" href="{{ params.postTextboxLinkUrl }}">{{ params.postTextboxLinkText }}</a>
        {% endif %}
    {% endset %}
    {% set field %}
        {% if params.label is defined and params.label and params.label.text is defined and params.label.text %}
            {{ onsLabel({
                "for": params.id,
                "id": params.label.id,
                "text": params.label.text,
                "classes": params.label.classes,
                "description": params.label.description,
                "attributes": params.label.attributes,
                "accessiblePlaceholder": params.accessiblePlaceholder
            }) }}
        {% endif %}

        {% if params.prefix is defined and params.prefix or params.suffix is defined and params.suffix %}
            {% if params.prefix is defined and params.prefix %}
                {% set prefixClass = " ons-input-type--prefix" %}
            {% endif %}

            <span class="ons-input-type{{ prefixClass }}">
                <span class="ons-input-type__inner">
                    {{ input | safe }}
                    {% set abbr = params.prefix or params.suffix %}
                    <abbr
                        class="ons-input-type__type ons-js-input-abbr"
                        aria-hidden="true"
                        title="{{ abbr.title }}"
                        {% if abbr.id is defined and abbr.id %} id="{{ abbr.id }}"{% endif %}
                        >{{ abbr.text or abbr.title }}</abbr>
                </span>
            </span>
        {% elif params.searchButton is defined and params.searchButton %}
            <span class="ons-grid--flex ons-search">
                {% call onsSearch({
                    "accessiblePlaceholder": params.accessiblePlaceholder,
                    "searchButton": {
                        "type": params.searchButton.type,
                        "text": params.searchButton.text,
                        "id": params.searchButton.id,
                        "attributes": params.searchButton.attributes,
                        "classes": params.searchButton.classes
                    }
                }) %}
                    {{ input | safe }}
                {% endcall %}
            </span>
        {% else %}
            {{ input | safe }}
        {% endif %}
    {% endset %}

    {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
        {% set charCheckField %}
            {% call onsCharLimit({
                "id": params.id ~ "-check",
                "limit": params.charCheckLimit.limit,
                "type": "check",
                "charCountSingular": params.charCheckLimit.charCountSingular,
                "charCountPlural": params.charCheckLimit.charCountPlural,
                "charCountOverLimitSingular": params.charCheckLimit.charCountOverLimitSingular,
                "charCountOverLimitPlural": params.charCheckLimit.charCountOverLimitPlural
            }) %}
                {{ field | safe }}
            {% endcall %}
        {% endset %}
    {% endif %}

    {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
        {% call onsMutuallyExclusive({
            "id": params.fieldId,
            "legend": params.legend,
            "legendClasses": params.legendClasses ~ "ons-js-input-legend",
            "description": params.description,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "checkbox": params.mutuallyExclusive.checkbox,
            "or": params.mutuallyExclusive.or,
            "deselectMessage": params.mutuallyExclusive.deselectMessage,
            "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective,
            "deselectCheckboxAdjective": params.mutuallyExclusive.deselectCheckboxAdjective,
            "error": params.error,
            "autosuggestResults": params.autosuggestResults
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% elif type == "hidden" %}
        {{ field | safe }}
    {% else %}
        {% call onsField({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "error": params.error
        }) %}
            {% if charCheckField is defined and charCheckField %}
                {{ charCheckField | safe }}
            {% else %}
                {{ field | safe }}
            {% endif %}
        {% endcall %}
    {% endif %}
{% endmacro %}
.ons-input-type {
  display: block;

  // Keep the entire component display block, but use inline-flex on inner to prevent the orange focus from going full width
  &__inner {
    display: inline-flex;
    position: relative;
  }

  // Double ampersand is needed to solve specificity issues
  & &__input {
    flex: 1 1 auto;
    position: relative;
    z-index: 1;

    &:focus {
      box-shadow: inset 0 0 0 1px $color-input;
    }
  }

  &__type[title] {
    background-color: $color-button-secondary;
    border: 1px solid $color-input;
    display: block;
    flex: 0 0 auto;
    font-size: 1rem;
    font-weight: $font-weight-bold;
    line-height: normal;
    padding: $input-padding-vertical $input-padding-horizontal * 2;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
  }

  &__input:focus + &__type[title]::after {
    border-radius: $input-radius;
    bottom: 0;
    box-shadow: 0 0 0 3px $color-focus;
    content: '';
    display: block;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
  }

  &:not(&--prefix) & {
    &__type[title] {
      border-left: 0;
      border-radius: 0 $input-radius $input-radius 0;
    }

    &__input {
      border-radius: $input-radius 0 0 $input-radius;
    }
  }

  &--prefix & {
    &__type[title] {
      border-radius: $input-radius 0 0 $input-radius;
      border-right: 0;
      order: 0;
    }

    &__input {
      border-radius: 0 $input-radius $input-radius 0;
      order: 1;
    }
  }
}

.ons-input {
  border: $input-border-width solid $color-input;
  border-radius: $input-radius;
  color: inherit;
  display: block;
  font-family: inherit;
  font-size: 1rem;
  line-height: 1rem;
  padding: $input-padding-vertical $input-padding-horizontal;
  position: relative;
  width: 100%;
  z-index: 3;

  &::-ms-clear {
    display: none;
  }

  @include mq(s) {
    &--text,
    &--select {
      &:not(.ons-input--block):not([class*='input--w-']) {
        width: $input-width;
      }
    }
  }

  &--text,
  &--textarea {
    // Prevent inner shadow on iOS
    appearance: none;
  }

  &:focus {
    box-shadow: 0 0 0 3px $color-focus, inset 0 0 0 1px $color-input;
    outline: none;
  }

  &:disabled {
    border-color: $color-grey-75;
    cursor: not-allowed;
  }

  &--error:not(:focus) {
    border: 1px solid $color-ruby-red;
    box-shadow: inset 0 0 0 1px $color-ruby-red;
  }

  &--with-description {
    margin-bottom: 0.55rem;
  }
}

// Text input widths
@include input-width('ons-input--w-{x}');

// Number input widths
@include input-width('ons-input-number--w-{x}', 0.54rem);

.ons-input--postcode {
  max-width: input-width-calc($chars: 5, $num-chars: 2, $spaces: 1);
  width: 100%;
}

.ons-input__helper {
  font-size: 0.8rem;
  font-weight: $font-weight-bold;
  margin-top: 0.2rem;
}

.ons-input--select {
  appearance: none;
  background: $color-white url('#{$static}/img/icons--chevron-down.svg') no-repeat center right 10px;
  background-size: 1rem;
  line-height: 1.3rem;
  padding: 0.39rem 2rem 0.39rem $input-padding-horizontal;

  &::-ms-expand {
    display: none;
  }
}

.ons-input--textarea {
  line-height: normal;
  resize: vertical;
  width: 100%;
}

.ons-input--block {
  display: block;
  width: 100%;
}

.ons-input--placeholder {
  background: transparent;
  &::placeholder {
    color: transparent;
  }
  &:valid:not(:placeholder-shown) {
    background-color: $color-white;
  }
  &:focus {
    background-color: $color-white;
  }
}

.ons-input--limit-reached:not(:focus) {
  border: $input-border-width solid $color-ruby-red;
}

.ons-input__limit {
  display: block;

  &--reached {
    color: $color-ruby-red;
  }
}

.ons-input--ghost {
  border: 2px solid rgba(255, 255, 255, 0.6);
  &:focus {
    border: 2px solid $color-input;
  }
}

.ons-input-search {
  @extend .ons-input--block;

  &--icon {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    background-position: 9px 7px;
    background-repeat: no-repeat;
    background-size: 24px 24px;
    padding-left: 2.4rem;

    &:focus,
    &:active,
    &:valid:not(:placeholder-shown) {
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 9px 7px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

How to check an input field

To help users enter something valid into an input field, you should:

  • allow them to paste into the input field
  • check they have entered something in the input field
  • check that what they have entered is valid
  • show an error message if they have not entered anything or what they have entered is not valid

Error messages

Use the correct errors pattern and show the error details above the input field.

<div class="ons-panel ons-panel--error ons-panel--no-title ons-u-mb-s" id="email-error">
  <span class="ons-u-vh">Error: </span>
  <div class="ons-panel__body">
    <p class="ons-panel__error">
      <strong>Enter an email address in a valid format, for example, name@example.com</strong>
    </p>
    <div class="ons-field">
      <label class="ons-label  ons-label--with-description " for="email">Email address
      </label>
      <span id="description-hint" class="ons-label__description  ons-input--with-description">
        This will not be stored and only used once to send your confirmation
      </span>
      <input type="email" id="email" class="ons-input ons-input--text ons-input-type__input ons-input--error" value="name.com" autocomplete="email" aria-describedby="description-hint" required="required" />
    </div>
  </div>
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "email",
        "type": "email",
        "autocomplete": "email",
        "value": "name.com",
        "label": {
            "text": "Email address",
            "description": "This will not be stored and only used once to send your confirmation"
        },
        "required": true,
        "error": {
            "id": "email-error",
            "text": "Enter an email address in a valid format, for example, name@example.com",
            "dsExample": isPatternLib
        }
    })
}}

If the input field is empty

Use “Enter [whatever the input is]”.
For example, “Enter an email address”.

If what is entered in the input field is not in a valid format

Use “Enter [whatever the input is] in a valid format, for example, [whatever the example is]”.
For example, “Enter an email address in a valid format, for example, name@example.com”.

Warning:

Only use an example if it is appropriate. For security reasons, it may not be appropriate to give an example of an access code.

There is specific guidance on how to ask users for access codes.

Research on this component

The character limit checker input variant was last user tested between February and September 2020 during research for Census 2021. The following findings were reported:

  • users who look down at a keyboard while typing may not notice the character counter
  • they expect to be stopped from entering too many characters

Help improve this component

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