Button States

2021-10-15

I was recently asked to style a html control panel interface for a client. It's essentially a grid of buttons that trigger a macro when pushed. One of the requirements was to have a visible change to the button when it was pressed.

Easy - I’ll said I'll just style out the :active CSS pseudo-class of the button, when a user clicks the button the :active pseudo-class will be toggled triggering a visual change on the button.

"The :active CSS pseudo-class represents an element (such as a button) that is being activated by the user. When using a mouse, "activation" typically starts when the user presses down the primary mouse button. The :active pseudo-class is commonly used on a & button elements" read more about :active pseudo class here

  .button--marco {
      background-color: $primary;
      color: white;
      border: 1px solid  $primary;
      padding: .95rem;
      @include transition;
      :active{
          border: 1px solid  $secondary;
          background-color: $secondary;
          color: white;       
      }
  }

This worked fine on my desktop, until I spun up a ngrok tunnel to test on my iphone. When I clicked the button, nothing happened. Hmmm. Turns out on ios the active states are sent so quickly that the down/active state is never received read more about this on stackoverflow

OK, so scrap that. I'll try a JS solution instead. Let's add an active class to the button when it's clicked. Expermented with the touchstart event for a while, adding an touchstart eventlistener to each button, but just scrolling the page on a touch screen was enough to trigger a button click - ba boww.

Finally settled on the following JS solution that listens for a mousedown event which triggers adding a class of active to the button followed by a timer that removes the class of active from the button after a short delay. Simulating a button ‘press’.

  document.querySelectorAll('.button--marco').forEach(function(button){
    button.addEventListener('mousedown', function (e){
      this.classList.add('active')
      if (this.classList.contains('active')) {
          setTimeout(() => { this.classList.remove('active') }, 500);
      }
    });
  })

See it in action here