Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create accessible combobox #64

Closed
terracoda opened this issue Nov 23, 2017 · 95 comments
Closed

Create accessible combobox #64

terracoda opened this issue Nov 23, 2017 · 95 comments
Assignees

Comments

@terracoda
Copy link
Contributor

terracoda commented Nov 23, 2017

Gathering resources for combobox:

@terracoda
Copy link
Contributor Author

terracoda commented Dec 6, 2017

@jessegreenberg , @zepumph , @mbarlow12 I have coded up an HTML select box. I have a feeling that might be way simpler than a custom combobox.

Please have a look in the html-sketches folder:
combobox/combobox-examples.html

@terracoda
Copy link
Contributor Author

I am going to put the design notes here in the issue and remove them from the example html.

@terracoda
Copy link
Contributor Author

terracoda commented Dec 7, 2017

Here's the draft visual highlighting
Functionally, both the Up and Down Arrow keys activate (pop-up) the collapased list of options. We may want to consider adjusting the visual prompt which only indicates the Up Arrow. Also in the visual, a selected item is not visually indicated. We may want to consider adding a visual checkmark (or some visual cue) to visually indicate which item is currently selected.

  1. Combobox gains focus with the Tab key (or other method). I don't think we need the checkmark on the selected item when the combobox is collapsed.
    molarity-combobox-focused

  2. Space key, Up Arrow, or Down Arrow open the list of solutes. The selected item has focus when the combobox is opened. A visual checkmark has been added for emphasis.
    molarity-selected-item-focused

  3. Focus highlight moves with Up and Down Arrow keys, and the selected item stays checked until a new selection is made by pressing the Enter key. Combobox closes when a new item is selected with the Enter key or Space key. Escape key also closes the list of options without making a change in selection.
    molarity-unselected-item-focused

Note: I updated key presses.

@terracoda
Copy link
Contributor Author

terracoda commented Dec 7, 2017

Example 1: HTML Design Pattern (working example)
The HTML design pattern uses the select element with a solutes codes as option elements. This works out-of-the-box with keyboard navigation and in Voice Over on Safari. The HTML size attribute can be used to make a designated number of options persist visually; however, this is undesirable in this case, and likely for most of PhET's interactive sim elements of this type (i.e., pop-up menus/comboboxes).

<label for="solute-options">Solute:</label>
<select id="solute-options" name="select">
  <option value="1">Drink mix</option>
  <option value="2">Colbalt (II) nitrate</option>
  <option value="3">Colbalt Chloride</option>
  <option value="4">Potassium dichromate</option>
  <option value="5">Gold (III) chloride</option>
  <option value="6">Potassium chromate</option>
  <option value="7">Nickel (II) chloride</option>
  <option value="8">Copper sulfate</option>
  <option value="9">Potassium permanganate</option>
  <option value="10">Potassium dichromate</option>
</select>

Example 2: ARIA 1.0 Design Pattern (non-working example)
The ARIA 1.1 design pattern is not yet supported. Both the 1.1 and 1.0 design patterns seem very complicated to me. And since our PDOM is visually hidden, I think the HTML solution in Example 1, above, is better for us.

In ARIA 1.1, the role=”combobox” is now a composite widget, meaning that it supports child element roles including active element roles embedded within it. However, the design pattern is too new to use.

In the ARIA 1.0 design pattern example below, there are three attributes on the focusable HTML input element that has the role="combobox" that have to be maintained dynamically.

These are:

  • aria-expanded
  • aria-controls
  • aria-activedescendant (Also, this attribute might not be allowed on the input element)

I think in our case the widget is read-only, as it is not editable, but placing the HTML5 readonly attribute on the input element removes the element from the Tab order. The code below is a non-working example. See Whatsock for a working example. This is the pattern I tried to follow.

<input id="solute-comboxbox" tabindex="0" aria-label="Solute" type="text"
 role="combobox" aria-expanded="false" aria-autocomplete="list" 
aria-controls="owned-solute-listbox" aria-activedescendent="" readonly>
<ul role="listbox" id="owned-solute-listbox">
  <li role="option" id="solute-option-01">Drink mix</li>
  <li role="option" id="solute-option-02">Colbalt (II) nitrate</li>
  <li role="option" id="solute-option-03">Colbalt Chloride</li>
  <li role="option" id="solute-option-04">Potassium dichromate</li>
  <li role="option" id="solute-option-05">Gold (III) chloride</li>
  <li role="option" id="solute-option-06">Potassium chromate</li>
  <li role="option" id="solute-option-07">Nickel (II) chloride</li>
  <li role="option" id="solute-option-08">Copper sulfate</li>
  <li role="option" id="solute-option-09">Potassium permanganate</li>
  <li role="option" id="solute-option-10">Potassium dichromate</li>
</ul>

@jessegreenberg
Copy link
Contributor

Thanks for working on this @terracoda, looks great and nice mockups!

I think we ran into trouble with the <select> box because we couldn't figure out how to hide the options in the parallel DOM. @zepumph am I remembering this correctly? @terracoda do you know of a way to make the select box drop down and options invisible with CSS?

If it can't be hidden, we might be stuck with an ARIA pattern.

@jessegreenberg
Copy link
Contributor

The Whatsock example is working really nicely, I agree it is very complicated.

@terracoda
Copy link
Contributor Author

terracoda commented Dec 7, 2017

For me the Whatsock example does not read out the individual options in VO and Safari. I only hear the selected language.

@jessegreenberg
Copy link
Contributor

Good to know @terracoda, thanks

@terracoda
Copy link
Contributor Author

terracoda commented Dec 7, 2017

@jessegreenberg can you explain what you mean by "hide the options in the PDOM".

  1. When using VO I only hear the options when the pop-up is open. I can't find any other options in the PDOM with the cursor keys when the pop-up is closed.
  2. Without VO on, however, when using the keyboard alone, I can arrow through the options with the Right and Left arrow keys without first popping up the list of options with the Up or Down Arrow keys.

@jessegreenberg, are you concerned about number 2? or something else? Number 2 seems natural to me, but might complicate implementation.

We might be able to restrict access to the options until the pop-up is actually open. I'd have to play with some things (hidden, aria-hidden, aria-expanded, css, etc). The size attribute doesn't seem to have any effect when set to 1.

I'll wait for @zepumph to comment with details because right now select and option seem to work beautifully for me out of the box.

@jessegreenberg
Copy link
Contributor

The problem is that the HTML select box needs to be invisible for sighted users. The last time we tried to use select, we couldn't figure out how to make the options box invisible. Take a look at this example:
https://jsfiddle.net/Lfbea747/show/
(You can see the HTML here: https://jsfiddle.net/Lfbea747)

Click in the grey box under 'Show' and press enter, and you will still see the box of options even though the select box is hidden. We couldn't figure out a way to hide this box with CSS, but there could certainly be a way we didn't think of. If we could use select I agree that would be the most straight forward.

@terracoda
Copy link
Contributor Author

terracoda commented Dec 7, 2017

@jessegreenberg, I don't understand how jsfiddle is supposed to work, but does
this work?

<div style="text-indent: -1000em;">
<label for="solute-options">Solute:</label>
<select id="solute-options" name="solute-options">
  <option value="1">Drink mix</option>
  <option value="2">Colbalt (II) nitrate</option>
  <option value="3">Colbalt Chloride</option>
  <option value="4">Potassium dichromate</option>
  <option value="5">Gold (III) chloride</option>      
  <option value="6">Potassium chromate</option>
  <option value="7">Nickel (II) chloride</option>
  <option value="8">Copper sulfate</option>
  <option value="9">Potassium permanganate</option>
  <option value="10">Potassium dichromate</option>
</select>
</div>

I think we may need to look seriously at how we are hiding the PDOM. Ideally, the entire thing is hidden in the same way.

@terracoda
Copy link
Contributor Author

@jessegreenberg I see what you mean now. The grey box seems to return visually, eventhough everything is moved off screen.

This needs some investigation.

@jessegreenberg
Copy link
Contributor

@terracoda I tried text-indent: -1000em;, it works great in Chrome! But the box still shows up at the very left edge of the screen in Safari.

@terracoda
Copy link
Contributor Author

@jessegreenberg, yes, same with Safari. The position of the box is very persistent.

@terracoda
Copy link
Contributor Author

@jessegreenberg, it finally disappeared with an option focus style applied. I'm not sure how robust this is, but I do not see the list of options or anything else in Safari.

In the head...

<style>
  .hide-me {
    text-indent: -1000em;
  }
  option:focus{
    display: block;
    text-indent: -1000em;  
  }  
  </style>

And in the body...

<div class="hide-me">
  <label for="solute-options">Solute:</label>
  <select id="solute-options" name="solute-options" size="1">
    <option value="1">Drink mix</option>
    <option value="2">Colbalt (II) nitrate</option>
    <option value="3">Colbalt Chloride</option>
    <option value="4">Potassium dichromate</option>
    <option value="5">Gold (III) chloride</option>
    <option value="6">Potassium chromate</option>
    <option value="7">Nickel (II) chloride</option>
    <option value="8">Copper sulfate</option>
    <option value="9">Potassium permanganate</option>
    <option value="10">Potassium dichromate</option>
  </select>
</div>

@terracoda
Copy link
Contributor Author

A negative left margin also seems to work.

@terracoda
Copy link
Contributor Author

For example:

<style>
  .hide-me {
    margin-left: -1000em;
  }
  option:focus{
    display: block;
    margin-left: -1000em;  
   }
  </style>

@jessegreenberg
Copy link
Contributor

jessegreenberg commented Dec 7, 2017

Awesome, thanks @terracoda! Unfortunately I tried the above in a JSFidddle and it didn't work for us in Safari, the box of options kept appearing. Would you mind sending a JSFiddle with it working in case we didn't use the right CSS? Or we could do a voice chat and share screens some time.

@terracoda
Copy link
Contributor Author

@jessegreenberg, did you try the actual HTML file on github?

@jessegreenberg
Copy link
Contributor

jessegreenberg commented Dec 7, 2017

I just tried https://github.com/phetsims/a11y-research/blob/master/html-sketches/combobox/combobox-examples.html, I can see the box in Safari 10.0.1 when I open it with spacebar.

EDIT: it is hidden correctly in Chrome.

@terracoda
Copy link
Contributor Author

Oh, I seemed to have been incorrect. I get the pop-up in Safari 11.01, too.

@terracoda
Copy link
Contributor Author

Would this "detaching" method work in our situation?
https://stackoverflow.com/questions/4398966/how-can-i-hide-select-options-with-javascript-cross-browser

@jessegreenberg
Copy link
Contributor

@terracoda I think that solution with .detach() is actually removing the elements from the DOM, not just hiding them. One thing I noticed was that the box looks different when the size attribute is used, and I am wondering if using size will prevent a box from being shown visually. @terracoda do you know if the size attribute will have an impact on how AT interpret this element?

@jessegreenberg
Copy link
Contributor

jessegreenberg commented Jan 26, 2018

@mbarlow12 looked into this example:
http://whatsock.com/tsg/Coding%20Arena/ARIA%20Comboboxes/ARIA%20Comboboxes%20(Simulated,%20Readonly)/demo.htm

It uses identical structure to HTML in #64 (comment), but with different elements, and different roles. If it sounds OK with supported AT we should use that.

EDIT: I also notice that it uses aria-labelledby to indicate selected menu option.

@mbarlow12
Copy link

mbarlow12 commented Jan 26, 2018

The above link dynamically adds the actual selector list content when the user interacts with the combobox. The structure of that content is:

<span role="combobox" class="cbox" tabindex="0" aria-haspopup="listbox" aria-controls="controlID" aria-expanded="false" aria-autocomplete="list">
    <span><!-- selected option here --></span>
</span>
<div>  <!-- main list container, this is not in the dom until the user opens the list -->
    <div></div>
    <div><!-- actual list -->
        <ul role="listbox" id="controlID">
            <li role="option" tabindex="-1" id="someid" data-value="The option text" ></li>
            <!-- ... -->
            <!-- ... -->
        </ul>
    </div>
    <a href="#"><!-- visually hidden link to close the selector box on keyboard nav --></a>
    <div></div><!-- don't know what this does -->
</div>  <!-- END: main list container -->
<select></select> <!-- the actual, persistent options -->

@jessegreenberg
Copy link
Contributor

That example sounds pretty good to me in FF + NVDA. We should discuss a few things with the team.

  • Which is preferable?
  • If we go with something more like the Combobox example from Whatsock, do we need to include the "Type to jump" feature? That includes complexity/cost significantly.

@terracoda
Copy link
Contributor Author

terracoda commented Feb 2, 2018

Great work @jessegreenberg and @mbarlow12, and thanks for looking at the whatsock example more deeply. I used it in my examples, too, but only really understand the HTML end of things.

In the article posted in #64 (comment), Garaventa discusses the current limitations of the changed combobox role, so we need to make sure it is meeting our needs in our supported platforms.

In #64 (comment)
@jessegreenberg said

After reading through the documentation for an ARIA combobox, I became convinced that PhET's combobox does not map to an ARIA combobox. ARIA comboboxes

I just want to repeat that type-ahead functionality is recommended in ARIA Practices, but not required.

I will test the latest examples with VO in the morning.

@terracoda
Copy link
Contributor Author

@jessegreenberg and @mbarlow12, regarding (#64 (comment)) I would gently caution against saying something has "identical structure" when in fact it contains "different elements, and different roles." These differences are key and exactly what we are trying to figure out.

That said, thanks for re-posting Garaventa's code. It has aria-haspopup="listbox" which I didn't include in the original HTML sketch I provided that was based on Garaventa's code. The code we have adapted thus far contains more native semantics than Garaventa's code.

We could try popping up a list with the role "listbox". That likely provides better interaction semantics than aria-haspopup="menu".

And @mbarlow12 points out in #64 (comment) that in the Garaventa example "the actual selector list content" is added dynamically. I think this is what we are trying to achieve.

I tested Garaventa's combobox with keyboard alone and with VO and the keyboard (I tested it in my early investigation and again just now.) The example works fine with the keyboard alone, but works inconsistently with VO turned on. The main issue being that I never hear the list of possible option items in the popped up list and I am never notified of my successful new selection.

Other issues are that activating the popped up list some times stops working, and sometimes I can't move through the options with the arrow keys, but these issues might be me and VO.

@terracoda
Copy link
Contributor Author

@jessegreenberg and @mbarlow12, I tested the Molarity pop-up menu in #64 (comment)

It sounds pretty good, except as we discussed last week the items are read out as menu items rather than options or list items.

@jessegreenberg, I am wondering if we can combine the approach you are taking and use the semantics either from ARIA or HTML or a combination that provide list semantics.

For example, something like this:
Changing aria-describedby to aria-labelledby which was suggested last week, and using listbox role instead of menu role for the poppedup options. And of course then the ul has to have the role listbox.

The option role can be placed directly on the list item or perhaps the <option> element can be used within a list item element. Not sure which will work best or what is valid use of the option element.

<div tabindex="-1" id="container-4-59-414-303-307">
  <button id="4-59-414-303-307" tabindex="0" aria-haspopup="listbox" aria-labelledby="description-4-59-414-303-307">Solute</button>
  <p tabindex="-1" id="description-4-59-414-303-307">‪Drink mix‬, selected</p>
</div>
<ul tabindex="-1" id="4-59-414-274-304" role="listbox" hidden="">
  <li tabindex="-1" id="4-59-414-274-304-313" role="option">Drink mix‬</li>
  <li tabindex="-1" id="4-59-414-274-304-315" role="option">‪Cobalt (II) nitrate‬</li>
  <li tabindex="-1" id="4-59-414-274-304-317" role="none">
      ...
</ul>

OR, something like this:

<div tabindex="-1" id="container-4-59-414-303-307">
  <button id="4-59-414-303-307" tabindex="0" aria-haspopup="listbox" aria-labelledby="description-4-59-414-303-307">Solute</button>
  <p tabindex="-1" id="description-4-59-414-303-307">‪Drink mix‬, selected</p>
</div>
<ul tabindex="-1" id="4-59-414-274-304" role="listbox" hidden="">
  <li tabindex="-1" id="4-59-414-274-304-313"><option value="1">Drink mix</option></li>
  <li tabindex="-1" id="4-59-414-274-304-315" ><option value="2">Cobalt (II) nitrate‬</option></li>
      ...
</ul>

@jessegreenberg
Copy link
Contributor

@terracoda said that the combobox role is not well supported by AT.
@terracoda said that text input is optional for a combobx.

@jessegreenberg
Copy link
Contributor

@terracoda said that a listbox has better semantics for this UI element. We will add the roles and change the elements so that it looks like the the first example listed in #64 (comment).

@jessegreenberg
Copy link
Contributor

We will try the first example @terracoda posted in the above comment and report back on how it sounds. That will help determine whether use roles that create a listbox or popup menu.

@jhung
Copy link

jhung commented Feb 2, 2018

https://www.w3.org/TR/wai-aria-1.1/#menu

"A type of widget that offers a list of choices to the user.

A menu is often a list of common actions or functions that the user can invoke. The menu role is appropriate when a list of menu items is presented in a manner similar to a menu on a desktop application.

To be keyboard accessible, authors SHOULD manage focus of descendants for all instances of this role, as described in Managing Focus.

Elements with the role menu have an implicit aria-orientation value of vertical."

Given the above description, then menu role could be viable assuming it provides a good experience for ATs.

@jessegreenberg
Copy link
Contributor

@mbarlow12 and @jessegreenberg worked on this today. We implemented the markup in the first example of #64 (comment).

@terracoda correctly predicted that aria-labelledby would override the accessible name for the pop up button, so we kept aria-describedby.

Here is a link, @terracoda what do you think?
https://www.colorado.edu/physics/phet/dev/html/molarity/1.4.0-a11yComboBox.4/phet/molarity_en_phet.html?accessibility

It sounds good with NVDA + FF, and @mbarlow12 also tested in Safari with VO. One benefit of this version is that items are correctly counted, so when the listbox is open, the AT might say
"List. Bullet, Drink Mix, One of Nine"

@terracoda
Copy link
Contributor Author

I like the counting semantics. Nice job @jessegreenberg and @mbarlow12! I'll review the keyboard interactions later.

Awesome combination of ideas!

@terracoda
Copy link
Contributor Author

terracoda commented Feb 8, 2018

@jessegreenberg, @mbarlow12, @jhung, @emily-phet I found an implemented listbox example from the W3C that uses markup and keypress patterns very similar to our approach.

The above example also uses two things to dynamically label the pop-up button:

  1. some prefixed text in a span element, and
  2. the dynamically selected option from the list.

This double-labeling technique, solves the short comings of aria-describedby in our case.

I suggest we dynamically change the label of the button to the selected solute, and use aria-labelledby to ensure both labels are read out. I suggest we use "Choose a Solute" for describing the interaction (rather than just "Solute") and then use the selected solute to dynamically label the button. This should mean that we do not need any on-demand help-text.

Here's a snippet of suggested HTML. I used word-based id's to explicitly demo the approach. You could use PhET's number id's, of course in your code.

<div tabindex="-1" id="container-4-59-414-303-307">
<span id="button-static-label-01">Choose a Solute:</span>
  <button id="button-dynamic-label-02" tabindex="0" aria-haspopup="listbox" aria-labelledby="button-static-label-01  button-dynamic-label-02">Drink Mix</button>
</div>
<ul tabindex="-1" id="4-59-414-274-304" role="listbox" hidden="">
  <li tabindex="-1" id="4-59-414-274-304-313" role="option">Drink mix‬</li>
...

I also re-read the specification for listbox and the recommendations in ARIA Practices to review guidance on not using auto-selection, and to review any recommended keyboard shortcuts.

It is totally fine to not use auto-selection. Our design case is better without auto-selection.

And regarding keyboard patterns and shortcuts, here's how I think our listbox should behave. (Checked items are already implemented, and I am not sure about ones marked with [?].)

  • Enter and Space key: If the listbox is NOT displayed, opens/expands the listbox and moves focus to selected option in the opened list. (Note that I can find anything about the Space key interaction, but that's how our example and the W3C example work. Space key works on a button, but not on a list.)
  • Enter key: If the listbox is displayed, collapses the listbox and moves focus to the button.
  • Escape key: If the listbox is displayed, collapses the listbox and moves focus to the button.
  • If an option is selected before the listbox receives focus, focus is set on the selected option.
  • Down Arrow: Moves focus to the next option.
  • Up Arrow: Moves focus to the previous option.

Optional, but useful for Molarity as there are more than 5 items

  • Home (Optional): Moves focus to first option.
  • End (Optional): Moves focus to last option.

Usually used on a multi-select listbox, but could be useful for Molarity since we are not using auto-selection. (decided against these interactions Feb 9th A11y meeting)

  • Shift + Down Arrow (Optional): Moves focus to and toggles the selected state of the next option.
  • Shift + Up Arrow (Optional): Moves focus to and toggles the selected state of the previous option.

Roles, States and Properties for out listbox

  • An element that contains or owns all the listbox options has role listbox.
  • Each option in the listbox has role option and is a DOM descendant of the element with role listbox.
  • If the listbox is not part of another widget, then it has a visible label referenced by aria-labelledby on the element with role listbox.
  • In a single-select listbox, the selected option has aria-selected set to true. Using aria-selected allows us to distinguish between the focused element and the selected element. The active element is managed by (document.activeElement). The selected element has aria-selected="true"

Things to consider for the issue about Visual Design (Moved to #73)

@terracoda
Copy link
Contributor Author

@jessegreenberg and @mbarlow12, I am un-assigning myself from this issue. A few more additions to our combobox and I think we will have a really nice one. Hopefully, AT and browsers will agree!

Let me know if you have any questions.

@terracoda terracoda removed their assignment Feb 8, 2018
@terracoda
Copy link
Contributor Author

This will be added into the accessibility API update, related to
phetsims/scenery#686 (comment)

@terracoda
Copy link
Contributor Author

Adding another resource which I just came across in case we need to refer to it later

@terracoda terracoda self-assigned this May 4, 2018
@terracoda
Copy link
Contributor Author

@terracoda to update html mock-up to use two labels with aria-labelledby.

@terracoda
Copy link
Contributor Author

@jessegreenberg , @mbarlow12 , @zepumph, @emily-phet I hear both labels when entering the listbox with the cursor keys, but not when placing focus with the Tab key. That's possibly how it is supposed to work.

I will review the one more time before committing. I think we have a relatively good solution.

@terracoda
Copy link
Contributor Author

terracoda commented May 10, 2018

I was reviewing the incorrect example. @jessegreenberg and I figured it out.
I added example 5 (d3e02e1) to the combobox examples sketches file and tested it. I hear both labels on focus. It sounds great!

@jessegreenberg or @mbarlow12 would you like to add the JS that makes the example actually collapse and open?

@terracoda
Copy link
Contributor Author

@jessegreenberg, @mbarlow12, and @zepumph. I updated the original file to include a 5th example, and then I decided to create a new file combobox.html that is final clean version. It sounds good.

I tried changing the span to an h3, but that change did not seem to have a good benefit in Voice Over. Anyways, we can explore further once implemented with some javascript.

Could one of you add the necessary JS to pop-up the listbox and to manage the necessary aria-attributes? Or would you rather just implement directly in Molarity?

@jessegreenberg and I thought it might be nice top have a working HTML example.

@terracoda terracoda removed their assignment May 14, 2018
@terracoda
Copy link
Contributor Author

I unassigned myself. @jessegreenberg and @mbarlow12, please re-assign if you have any questions.

@terracoda
Copy link
Contributor Author

When we are ready to implement the combobox, we should re-compaare our code with the approach described here
https://www.levelaccess.com/differences-aria-1-0-1-1-changes-rolecombobox/

@terracoda
Copy link
Contributor Author

@zepumph has implemented the first accessible combobox in phetsims/sun#314, so I am closing this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants