A HTML5 aria-expanded toggle button

A lot is possible with web standards without using frameworks or libraries. Support in browsers has become very good, compared to a couple of years ago. With the introduction of the document.querySelector, CSS selectors can be used with JavaScript in almost all browsers, including IE8. In the past you would have needed a library like jQuery. Also you can style elements by attribute in CSS.

Using these capabilities, you can create a simple toggle button. For starters use a button for a button. Seems obvious, but it is becoming normal to use a div element for everything. The button is keyboard accessible by default. You get that for free when using semantic HTML. By adding role="button" you make it independent from a form, this way not causing a submit on click. You also could use a <a href> link, but this would not really link to a resource. Making a button the better choice. And with CSS you can make it look like something different than a button, but keeping the accessibility advantages. Using type="button" does the same, but role="button" can be applied to more than only a button

Next step is to add an aria-expanded="false" WAI-ARIA attribute. This tells a screen reader that the related content is hidden. You could add aria-controls to identify the related content, but Aria-Controls is Poop.

Hiding is done by adding the hidden attribute. This automatically hides the element without CSS. CSS is only needed for old browsers that do not have support for hidden attribute. With JavaScript you then can add the toggle behaviour by adding/removing the attribute.

The elements can be turned into a reusable component. I like to use the dl element, because it consist of a dt element for titles and a dd element for descriptions. By adding a class you make it readily usable without a framework. And you also can add multiple instances.

Below you see the HTML, CSS and JavaScript code to put it all together. You could apply role="button" and aria-expanded="false" attribute directly to the dt element, but a button is automatically clickable by users.

<dl class="expandable">
  <dt><button aria-expanded="false" type="button">Click me</button></dt>
  <dd hidden>Expanded content</dd>
</dl>
<script>
;(function () {
  var expandables = document.querySelectorAll('.expandable [aria-expanded]')
  for(i = 0; i < expandables.length ; i++) {
    expandables[i].addEventListener('click', function () {
      var expanded = this.getAttribute('aria-expanded')
      if (expanded === 'false') {
        this.setAttribute('aria-expanded', 'true')
      } else {
        this.setAttribute('aria-expanded', 'false')
      }
      var content = this.parentNode.parentNode.querySelector('dd')
      if (content) {
        content.getAttribute('hidden')
        if (typeof content.getAttribute('hidden') === 'string') {
          content.removeAttribute('hidden')
        } else {
          content.setAttribute('hidden', 'hidden')
        }
      }
    })
  }
})()
</script>
<style>
[hidden] {display: none;} /* Fallback for old browsers */
</style>

See the demo for a working example.

I hope you this showed you that web standards are quite powerful and you do not always have to use a framework.