Skip to main content

Ask users for Numeric values

How to ask for numeric values such as quantities, financial figures, percentages or units of measurement.

<div class="ons-field">
  <label class="ons-label  " for="number">Number of bedrooms
  </label>
  <input type="text" id="number" class="ons-input ons-input--text ons-input-type__input ons-input-number--w-2" pattern="[0-9]*" inputmode="numeric" autocomplete="off" min />
</div>
<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>
<div class="ons-field">
  <label class="ons-label  " for="suffixed-percent">People with internet access
  </label>
  <span class="ons-input-type">
    <span class="ons-input-type__inner">
      <input type="text" id="suffixed-percent" class="ons-input ons-input--text ons-input-type__input ons-input-number--w-3" title="per cent" pattern="[0-9]*" inputmode="numeric" autocomplete="off" required="required" />
      <abbr class="ons-input-type__type ons-js-input-abbr" aria-hidden="true" title="per cent">%</abbr>
    </span>
  </span>
</div>
<div class="ons-field">
  <label class="ons-label  ons-label--with-description " for="suffixed-centimetres">Length
  </label>
  <span id="description-hint" class="ons-label__description  ons-input--with-description">
    For example enter 1250 for 12.5m
  </span>
  <span class="ons-input-type">
    <span class="ons-input-type__inner">
      <input type="text" id="suffixed-centimetres" class="ons-input ons-input--text ons-input-type__input ons-input-number--w-4" title="centimetres" pattern="[0-9]*" inputmode="numeric" autocomplete="off" aria-describedby="description-hint" required="required" />
      <abbr class="ons-input-type__type ons-js-input-abbr" aria-hidden="true" title="centimetres">cm</abbr>
    </span>
  </span>
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "number",
        "type": "number",
        "width": "2",
        "autocomplete": "off",
        "attributes": {
            "min": 0
        },
        "label": {
            "text": "Number of bedrooms"
        }
    })
}}
{{
    onsInput({
        "id": "prefixed",
        "type": "number",
        "width": "6",
        "autocomplete": "off",
        "required": true,
        "label": {
            "text": "Your annual salary before taxes"
        },
        "prefix": {
            "title": "British pounds (GBP)",
            "text": "£"
        }
    })
}}
{{
    onsInput({
        "id": "suffixed-percent",
        "type": "number",
        "width": "3",
        "autocomplete": "off",
        "required": true,
        "label": {
            "text": "People with internet access"
        },
        "suffix": {
            "title": "per cent",
            "text": "%"
        }
    })
}}
{{
    onsInput({
        "id": "suffixed-centimetres",
        "type": "number",
        "width": "4",
        "autocomplete": "off",
        "required": true,
        "label": {
            "text": "Length",
            "description": "For example enter 1250 for 12.5m"
        },
        "suffix": {
            "title": "centimetres",
            "text": "cm"
        }
    })
}}
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,
                        "iconType": params.searchButton.iconType
                    }
                }) %}
                    {{ 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' viewBox='0 0 12 12' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z'/%3E%3C/svg%3E");
    background-position: 12px 10px;
    background-repeat: no-repeat;
    background-size: 18px 18px;
    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' viewBox='0 0 12 12' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 12px 10px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

When to use this pattern

This pattern should be used when you need to ask the user for numeric values such as:

  • the quantity of objects or people (for example, bedrooms, visitors or employees)
  • financial figures (for example, salaries, expenditure or turnover)
  • estimated percentages (for example, the percentage of female employees)
  • volumes and measurements (for example, the size of a workspace)

How to use this pattern

Use the input component macro and set the type parameter to “number” to add the correct pattern to trigger a numeric keypad on touch devices.

Warning:
Server-side validation on number type input is not currently supported by all browsers 
If you are manually using the component’s html, make sure you set the attributes type="text", inputmode="numeric" and pattern="[0-9]*".

Use appropriately sized number inputs

You can use the input width override utility class to set the width of the input when the maximum size of the answer is known. The class won’t limit the number of digits the user can enter, but will help the user understand what they should enter.

Numbers

<div class="ons-field">
  <label class="ons-label  " for="number">Number of bedrooms
  </label>
  <input type="text" id="number" class="ons-input ons-input--text ons-input-type__input ons-input-number--w-2" pattern="[0-9]*" inputmode="numeric" autocomplete="off" min />
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "number",
        "type": "number",
        "width": "2",
        "autocomplete": "off",
        "attributes": {
            "min": 0
        },
        "label": {
            "text": "Number of bedrooms"
        }
    })
}}
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,
                        "iconType": params.searchButton.iconType
                    }
                }) %}
                    {{ 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' viewBox='0 0 12 12' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z'/%3E%3C/svg%3E");
    background-position: 12px 10px;
    background-repeat: no-repeat;
    background-size: 18px 18px;
    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' viewBox='0 0 12 12' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 12px 10px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

Prefixed values

Use the prefix parameter to specify the abbreviated prefix unit. For example, “£”, and the title parameter to specify the full name of the prefix unit for example, “British pounds (GBP)”.

<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,
                        "iconType": params.searchButton.iconType
                    }
                }) %}
                    {{ 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' viewBox='0 0 12 12' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z'/%3E%3C/svg%3E");
    background-position: 12px 10px;
    background-repeat: no-repeat;
    background-size: 18px 18px;
    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' viewBox='0 0 12 12' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 12px 10px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

Suffixed values

Use the suffix parameter to specify the abbreviated suffix unit for example, “cm”, and the title parameter to specify the full name of the suffix unit for example, “centimetres”.

<div class="ons-field">
  <label class="ons-label  " for="suffixed-per-cent">People with internet access
  </label>
  <span class="ons-input-type">
    <span class="ons-input-type__inner">
      <input type="text" id="suffixed-per-cent" class="ons-input ons-input--text ons-input-type__input ons-input-number--w-3" title="per cent" pattern="[0-9]*" inputmode="numeric" autocomplete="off" required="required" />
      <abbr class="ons-input-type__type ons-js-input-abbr" aria-hidden="true" title="per cent">%</abbr>
    </span>
  </span>
</div>
<div class="ons-field">
  <label class="ons-label  ons-label--with-description " for="suffixed-centimetres">Length
  </label>
  <span id="description-hint" class="ons-label__description  ons-input--with-description">
    For example enter 1250 for 12.5m
  </span>
  <span class="ons-input-type">
    <span class="ons-input-type__inner">
      <input type="text" id="suffixed-centimetres" class="ons-input ons-input--text ons-input-type__input ons-input-number--w-4" title="centimetres" pattern="[0-9]*" inputmode="numeric" autocomplete="off" aria-describedby="description-hint" required="required" />
      <abbr class="ons-input-type__type ons-js-input-abbr" aria-hidden="true" title="centimetres">cm</abbr>
    </span>
  </span>
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "suffixed-per-cent",
        "type": "number",
        "width": "3",
        "autocomplete": "off",
        "required": true,
        "label": {
            "text": "People with internet access"
        },
        "suffix": {
            "title": "per cent",
            "text": "%"
        }
    })
}}
{{
    onsInput({
        "id": "suffixed-centimetres",
        "type": "number",
        "width": "4",
        "autocomplete": "off",
        "required": true,
        "label": {
            "text": "Length",
            "description": "For example enter 1250 for 12.5m"
        },
        "suffix": {
            "title": "centimetres",
            "text": "cm"
        }
    })
}}
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,
                        "iconType": params.searchButton.iconType
                    }
                }) %}
                    {{ 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' viewBox='0 0 12 12' fill='%23ffffff'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z'/%3E%3C/svg%3E");
    background-position: 12px 10px;
    background-repeat: no-repeat;
    background-size: 18px 18px;
    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' viewBox='0 0 12 12' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M11.86 10.23 8.62 6.99a4.63 4.63 0 1 0-6.34 1.64 4.55 4.55 0 0 0 2.36.64 4.65 4.65 0 0 0 2.33-.65l3.24 3.23a.46.46 0 0 0 .65 0l1-1a.48.48 0 0 0 0-.62Zm-5-3.32a3.28 3.28 0 0 1-2.31.93 3.22 3.22 0 1 1 2.35-.93Z'/%3E%3C/svg%3E");
    }
    &:focus,
    &:active {
      background-position: 12px 10px;
      box-shadow: 0 0 0 3px $color-focus;
    }
  }
}

How to check numeric input

To help users enter valid input, you should:

  • allow them to paste the number
  • check they have entered something in the number 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 number field.

<div class="ons-panel ons-panel--error ons-panel--no-title ons-u-mb-s" id="percentage-error">
  <span class="ons-u-vh">Error: </span>
  <div class="ons-panel__body">
    <p class="ons-panel__error">
      <strong>Enter a number, for example, 6</strong>
    </p>
    <div class="ons-field">
      <label class="ons-label  " for="percentage">People with internet access
      </label>
      <span class="ons-input-type">
        <span class="ons-input-type__inner">
          <input type="text" id="percentage" class="ons-input ons-input--text ons-input-type__input ons-input--error ons-input-number--w-3" title="per cent" value="six" pattern="[0-9]*" inputmode="numeric" autocomplete="off" required="required" />
          <abbr class="ons-input-type__type ons-js-input-abbr" aria-hidden="true" title="per cent">%</abbr>
        </span>
      </span>
    </div>
  </div>
</div>
{% from "components/input/_macro.njk" import onsInput %}
{{
    onsInput({
        "id": "percentage",
        "type": "number",
        "width": "3",
        "autocomplete": "off",
        "value": "six",
        "label": {
            "text": "People with internet access"
        },
        "required": true,
        "suffix": {
            "title": "per cent",
            "text": "%"
        },
        "error": {
            "id": "percentage-error",
            "text": "Enter a number, for example, 6",
            "dsExample": isPatternLib
        }
    })
}}

If any of the number fields are empty

Use “Enter [whatever the numeric value is]”.
For example, “Enter the percentage of people with internet access”.

If what is entered in one of the fields is not a number

Use “Enter a number, for example, [appropriate number]”.
For example, “Enter a number, for example, 7”.

If what is entered in one of the fields is above the maximum value for that field

Use “Enter a number that is less than [whatever the maximum is]”.
For example, “Enter a number that is less than 101” for percentages.

If what is entered in one of the fields is below the minimum value for that field

Use “Enter a number that is greater than [whatever the maximum is]”.
For example, “Enter a number that is greater than 10”.

If what is entered is not within the required range for that field set

Use “Enter a number that is between [whatever the minimum is] and [whatever the maximum is]”.
For example, “Enter a number that is between 1 and 10”.

Help improve this pattern

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