input value modificator

The value modificator element can be used for numeric input. It features an optional label as well as success, error and warning states.

The plus and minus icons on the right allow to move the value up or down by the step size.

Min and max attributes can also be defined to limit the range of allowed input.

In order for the label to work correctly, the input tag needs a unique id attribute and the label tag needs the same id for the for attribute.
According to the Web Content Accessibility Guidelines (WCAG), it is highly reccomended to use a label together with the value modificator.

component variations

Input value modificator

<div class="a-value-modificator">
  <input type="number" min="0" max="100" step="5" id="1" />
  <button
    type="button"
    class="a-value-modificator__minus-button"
    aria-label="Minus"
  >
    <i class="a-icon boschicon-bosch-ic-less-minimize"></i>
  </button>
  <button
    type="button"
    class="a-value-modificator__plus-button"
    aria-label="Plus"
  >
    <i class="a-icon boschicon-bosch-ic-add"></i>
  </button>
</div>

Input value modificator disabled

<div class="a-value-modificator -disabled">
  <input type="number" min="0" max="100" step="5" id="2" disabled="" />
  <button
    type="button"
    class="a-value-modificator__minus-button"
    disabled=""
    aria-label="Minus"
  >
    <i class="a-icon boschicon-bosch-ic-less-minimize"></i>
  </button>
  <button
    type="button"
    class="a-value-modificator__plus-button"
    disabled=""
    aria-label="Plus"
  >
    <i class="a-icon boschicon-bosch-ic-add"></i>
  </button>
</div>

Input value modificator with label readonly

<div class="a-value-modificator -readonly">
  <label for="3">
    Lorem ipsum dolor, sit amet consectetur adipisicing elit. Vitae omnis labore
    consequatur distinctio provident voluptatum nisi recusandae quod asperiores
    quo, architecto cum nesciunt nemo, vel minima possimus et blanditiis
    numquam, cupiditate deserunt fugit delectus earum ducimus alias! Quam atque
    laborum tempore alias magnam tenetur fuga facere totam, harum, ipsa sit!
  </label>
  <input type="number" min="0" max="100" step="5" id="3" readonly="" />
  <button
    type="button"
    class="a-value-modificator__minus-button"
    disabled=""
    aria-label="Minus"
  >
    <i class="a-icon boschicon-bosch-ic-less-minimize"></i>
  </button>
  <button
    type="button"
    class="a-value-modificator__plus-button"
    disabled=""
    aria-label="Plus"
  >
    <i class="a-icon boschicon-bosch-ic-add"></i>
  </button>
</div>

Input value modificator with label

<div class="a-value-modificator">
  <label for="4">
    Lorem ipsum dolor, sit amet consectetur adipisicing elit. Vitae omnis labore
    consequatur distinctio provident voluptatum nisi recusandae quod asperiores
    quo, architecto cum nesciunt nemo, vel minima possimus et blanditiis
    numquam, cupiditate deserunt fugit delectus earum ducimus alias! Quam atque
    laborum tempore alias magnam tenetur fuga facere totam, harum, ipsa sit!
  </label>
  <input type="number" min="0" max="100" step="5" id="4" />
  <button
    type="button"
    class="a-value-modificator__minus-button"
    aria-label="Minus"
  >
    <i class="a-icon boschicon-bosch-ic-less-minimize"></i>
  </button>
  <button
    type="button"
    class="a-value-modificator__plus-button"
    aria-label="Plus"
  >
    <i class="a-icon boschicon-bosch-ic-add"></i>
  </button>
</div>

additional content

Instance API

The instance API can be accessed by the component property of the dialog element.

Method description
setValue(value: number) sets the value of the input field of the value modificator. Be aware with this the value can also be sets to values, which can not be achieved via the plus/minus buttons. E.g. steps are set to 5, with the usage of setValue(11), 11 will be the value. Using than the buttons will change it to 6 or 16 accordingly.

Event API

Event Handlers can be registered by calling component.addEventListener(name, callback). They can be removed by calling component.removeEventListener(name, callback) with the same arguments. Also, addEventListener returns an unsubscription function that, once called, achieves the same.

Event Name parameters description
onInput value: number Will be triggered when the value of the input is set and contain the new value

styles SCSS

/* stylelint-disable no-descending-specificity */
.a-value-modificator {
  position: relative;
  height: 3rem;
  margin: 0.5rem;
  display: flex;

  &.-readonly {
    .a-value-modificator__minus-button,
    .a-value-modificator__plus-button {
      background-color: var(--plain__enabled__fill__default);
    }
  }

  &.-disabled {
    .a-value-modificator__minus-button,
    .a-value-modificator__plus-button,
    .a-value-modificator__minus-button:disabled,
    .a-value-modificator__plus-button:disabled {
      color: var(--neutral__disabled__front__default);
      background-color: var(--neutral__disabled__fill__default);
      border-bottom-color: var(--neutral__disabled__front__default);
    }
  }

  input[type='number']::-webkit-inner-spin-button,
  input[type='number']::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
  }

  input[type='number'] {
    -moz-appearance: textfield;
    box-shadow: none;
  }

  input {
    height: 100%;
    // minus two icons with margins
    width: calc(100% - 6rem);
    border: 0;
    background-color: var(--neutral__enabled__fill__default);
    border-bottom: 0.0625rem solid var(--neutral__enabled__front__default);
    color: var(--neutral__enabled__front__default);
    line-height: 1.5rem;
    font-size: 1rem;
    padding: 0 1rem;

    &:disabled {
      color: var(--neutral__disabled__front__default);
      background-color: var(--neutral__disabled__fill__default);
      border-bottom-color: var(--neutral__disabled__front__default);
    }

    &[readonly],
    &:hover[readonly],
    &:active[readonly],
    &:focus[readonly],
    &:disabled[readonly] {
      background-color: var(--plain__enabled__fill__default);
      pointer-events: none;
    }

    &:focus {
      background-color: var(--neutral__focused__fill__default);
    }

    &:focus-visible {
      border: 3px solid var(--plain__enabled__front__default);
      outline: 3px solid var(--background);
      outline-offset: -6px;
      padding-inline: 13px;
      padding-block-end: 1px;
    }
  }

  label {
    position: absolute;
    align-self: flex-start;
    margin: 0.25rem 1rem auto 1rem;
    font-size: 0.75rem;
    max-width: calc(100% - 7rem);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--neutral__enabled__front__default);

    + input {
      padding-top: 1.125rem;
      padding-bottom: 0.3125rem;

      &:focus-visible {
        padding-block-end: 0.375rem;
      }
    }
  }

  &__minus-button,
  &__plus-button {
    border: 0;
    cursor: pointer;
    background-color: var(--neutral__enabled__fill__default);
    color: var(--neutral__enabled__front__default);
    width: 3rem;
    height: 3rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-bottom: 0.0625rem solid var(--neutral__enabled__front__default);

    &:hover {
      background-color: var(--neutral__enabled__fill__hovered);
    }

    &:active {
      background-color: var(--neutral__enabled__fill__pressed);
    }

    &:focus-visible {
      @include focus-inside;
    }

    &:disabled {
      color: var(--neutral__disabled__front__default);
      background-color: var(--neutral__enabled__fill__default);
      pointer-events: none;
    }
  }
}