Building accessible-app.com: New menu button for Accessibook's shopping cart

My original intention for the next article of the #accessibleapp project was to look at "notifying users of changes", especially regarding content reloads that do not trigger a full page reload (that lack of a full page reload in Single Page Apps leads to the necessity to come up with a routing strategy). Putting an item into a shopping cart in an e-commerce context without redirecting to a proper shopping cart page can be such an asynchronous change of content, and could be hard to notice for, e.g., screen reader users. Leonie Watson writes about this problem:

The update is easy to see at a glance, but not at all easy with a screen reader. First you have to discover that something has changed, then you have to find out where it happened. Even once you know this, you still need to move focus back and forth between the summary and the product information, every time you add an item to the basket.

And at the same time she points to a solution strategy: Using aria-live regions to announce changes on the site or in the app that happened without proper page reload. So, I thought, the next thing to add to my accessible example app "Accessibooks" is shopping cart functionality - it's a fake shopping app after all. While building the feature it turned out that I touch on other accessibility topics, and each one of them is worth their own blog post:

  • Animations, and how to turn them off via setting within the app, or inside your operating system
  • Aforementioned use of aria-live regions after shopping cart changes
  • And how to markup the shopping cart as a menu widget in general

The following part of this article will be about the latter.

Details/Summary

When you read my last #accessibleapp article you will notice that I added an update regarding the WAI ARIA Authoring Practice I based my "vue-menu-button" on. The situation is not very clear, there are many experiences with and opinions on this subject. Until there is more user data available on this subject I decided to remove the menu/menu-item pattern from the account button and use a more simple solution instead: details and summary (I wrote a small note on how GitHub is using these elements for their menu structure - although relying on the menu/menuitem pattern). Especially regarding the fact that I - until now - only added links to that specific menu.

So, my next step was to implement <details> and <summary> in Vue. Which was very easy, due to the elements on-board toggling behavior. Here is the structure:

<details ref="details">
    <summary>I'm the trigger</summary>
    <div class="content">
        Here goes the content that is initially hidden
        but visible once details has the open attribute
    </div>
</details>

What happens on the HTML side of things is: once the summary element is activated (via click, touch, enter, space-bar) the open attribute is added to the details element as a whole. To achieve a "menu look" I only had to style the content that I intended to use as "menu content"

[open] .content {
  position: absolute;
  background-color: #ffffff;
  min-width: 320px;
  padding: 10px 0;
  border: 1px solid #2368a2;
  animation: slide-down 0.2s ease;
  box-shadow: 4px 4px 6px 0 #6665654d;
}

The summary, element, when unstyled, includes a caret. You can remove it via:

summary {
  list-style: none;
}

One last thing I added was a feature which I had built for the menu button: that a click outside my details/summary construct removes the open attribute and, therefore, closes the menu. This was the first "real" part where I had to deal with Vue and JavaScript, and it was as easy as:

<script>
export default {
  name: "NavigationMenu",
  created() {
    // On components creation, add click event listener to document
    document.addEventListener("click", this.documentClick);
  },
  methods: {
    documentClick(e) {
      // Get the details element
      let el = this.$refs.details;

      // Check if click happend inside component
      let target = e.target;

      // If not, close
      if (el !== target && !el.contains(target)) {
         this.$refs.details.removeAttribute("open");
      }
    }
  }
};
</script>

I will use this approach both for the account (which will contain links) and the shopping cart button. Thats why I made a component out of the structure, styling and "behavior" I described in this article.

You can see the aforementioned buttons in action on https://vuejs.accessible-app.com. Actually, there is a lot more to see (and to hear). But as I wrote above - I will explain the other features of the shopping cart in separate articles.

For now, let's wrap up. Once I find the time, it's likely that I release my light Vue wrapper for details/summary as a discrete component, that also deals with the browser support by adding a polyfill. But at this point in the example app's life using details/summary for these menu purposes feels right overall.