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

a11y support for tri-state #124

Open
userquin opened this issue May 26, 2016 · 8 comments
Open

a11y support for tri-state #124

userquin opened this issue May 26, 2016 · 8 comments

Comments

@userquin
Copy link

userquin commented May 26, 2016

Provide tri-state support, not only true or false checked value, allow mixed:

https://www.w3.org/TR/wai-aria/roles#checkbox

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

@notwaldorf
Copy link
Contributor

We've had an in depth discussion about this on this thread as to why this shouldn't be a property of the checkbox, but a separate element in itself: #33

The feature request for a checkable node element is here: PolymerElements/paper-elements#24

@notwaldorf
Copy link
Contributor

Re-opening, since this is an a11y issue, not about the tri-state UI.

@userquin userquin changed the title AA support for tri-state a11y support for tri-state May 31, 2016
@bicknellr
Copy link
Contributor

I don't think there's any mandate that checkboxes support a mixed state. In fact, there's explicit mention of a dual-state checkbox in the WAI-ARIA Authoring Practices document. That being said, I don't understand the opposition to adding a mixed state to paper-checkbox. There might not be a mixed state for checkboxes in the current Material Design spec but are we really going to land that far off if we add one ourselves? It's a rectangle.

@admwx7
Copy link

admwx7 commented Nov 10, 2016

Any chance of getting work towards this in the near future? If there's a design plan I might be able to get some time to contribute a start on it.

@userquin
Copy link
Author

userquin commented Nov 12, 2016

Here some hints, based on paper-checkbox (1.3.0, not last but...) (copy it to your component and make these changes):

NOTE: I use this element as a selector not as a checkbox input, so I don't need the invalid property, in fact, I only use it in table headers to change context toolbar for selected rows (delete selected row for example). That being said, the invalid property is ignored.

@notwaldorf maybe this can help you ;)...

Modify aria-checked hostAttribute to be a string instead a boolean and add a property called checked :

hostAttributes: {
    role: 'checkbox',
    'aria-checked': 'false',
    tabindex: 0
},
...
properties {
    ...
    /**
     * Gets or sets the state, `true` is checked, `false` is unchecked, `mixed` for partially checked.
     */
    checked: {
      type: String,
      value: 'false',
      reflectToAttribute: true,
      notify: true,
      observer: '_checkedChanged'
    }
}

Add _checkedChanged method and change _computeCheckboxClass (REMOVE invalid parameter). Also override _activeChanged and _buttonStateChanged methods:

_activeChanged: function(active, ariaActiveAttribute) {
  this._changedButtonState();
},
/**
 * Synchronizes the element's `active` and `checked` state.
 */
_buttonStateChanged: function() {
},
_computeCheckboxClass: function (checked) {
    var className = null;
    if (checked === 'true') {
        className = 'checked';
    } else if (checked === 'mixed') {
        className = 'mixed';
    }
    return className;
},
/**
 * Fire `iron-change` when the checked state changes.
 */
_checkedChanged: function() {
  this.active = this.checked === 'true' || this.checked === 'mixed';
    if (this.checked == 'true') {
      this.setAttribute(this.ariaActiveAttribute, 'true');
    } else if (this.checked == 'mixed') {
      this.setAttribute(this.ariaActiveAttribute, 'mixed');
    } else {
        this.removeAttribute(this.ariaActiveAttribute);
    }
    if (this.hasRipple()) {
     if (this.checked === 'true' || this.checked === 'mixed') {
       this._ripple.setAttribute('checked', '');
     } else {
       this._ripple.removeAttribute('checked');
     }
   }
    this.fire('iron-change');
},

Now component styles:

OLD CODE:

#checkbox.checked #checkmark {
    -webkit-animation: checkmark-expand 140ms ease-out forwards;
    animation: checkmark-expand 140ms ease-out forwards;
}
#checkmark {
    position: absolute;
    width: 36%;
    height: 70%;
    border-style: solid;
    border-top: none;
    border-left: none;
    border-right-width: calc(2/15 * var(--calculated-paper-checkbox-size));
    border-bottom-width: calc(2/15 * var(--calculated-paper-checkbox-size));
    border-color: var(--paper-checkbox-checkmark-color, white);
    -webkit-transform-origin: 97% 86%;
    transform-origin: 97% 86%;
    box-sizing: content-box; /* protect against page-level box-sizing */
}

NEW CODE:

:host(:not([aria-checked=mixed])) #checkbox.checked #checkmark {
    -webkit-animation: checkmark-expand 140ms ease-out forwards;
    animation: checkmark-expand 140ms ease-out forwards;
}
:host(:not([aria-checked=mixed])) #checkmark {
    position: absolute;
    width: 36%;
    height: 70%;
    border-style: solid;
    border-top: none;
    border-left: none;
    border-right-width: calc(2 / 15 * var(--calculated-paper-checkbox-size));
    border-bottom-width: calc(2 / 15 * var(--calculated-paper-checkbox-size));
    border-color: var(--paper-checkbox-checkmark-color, white);
    -webkit-transform-origin: 97% 86%;
    transform-origin: 97% 86%;
    box-sizing: content-box; /* protect against page-level box-sizing */
}

Add these new styles for mixed box:

:host([aria-checked=mixed]) #checkmark {
    border-bottom: 2px solid var(--primary-text-color);
    width: 60%;
    height: 45%;
    margin-left: 3px;
    -webkit-animation: mixed-expand 140ms ease-out forwards;
    animation: mixed-expand 140ms ease-out forwards;
}
@-webkit-keyframes mixed-expand {
    0% {
        -webkit-transform: scale(0, 0);
    }
    100% {
        -webkit-transform: scale(1, 1);
    }
}
@keyframes mixed-expand {
    0% {
        transform: scale(0, 1);
    }
    100% {
        transform: scale(1, 1);
    }
}

And finally adjust html, comment invalid arg in _computeCheckboxClass call:

<div id="checkboxContainer">
  <div id="checkbox" class$="[[_computeCheckboxClass(checked/*, invalid*/)]]">
    <div id="checkmark" class$="[[_computeCheckmarkClass(checked)]]"></div>
  </div>
</div>

imagen

imagen-mixed

imagen-checked

@MMJM
Copy link

MMJM commented Dec 9, 2016

+1 for a tri-state checkbox. In our case of legal questions there must be a specific indication of yes or no so there is a case for an unanswered state.

@adalinesimonian
Copy link

I rescind aspects of my opinion on #33 that say that this should not be implemented, seeing as it is part of WCAG. However, I still feel that, following good UX principles, a tri-state checkbox is not the best choice for most applications.

I would suggest, @MMJM, using a dropdown with something along the lines of "Unanswered," "Yes," and "No," because it would be much more explicit to the user, and would make exactly what they're answering much clearer. With a tri-state checkbox, the third state, while natural in the context of @userquin's example above, or in the context of a tree, I believe would be unnatural in the context of answering a question where there are really three "answers."

@notwaldorf, when you say this is about a11y, but not about the UI, what do you mean exactly? If we were to implement a mixed state, that would have to reflect in the UI, no?

@andrewillard
Copy link

andrewillard commented Feb 6, 2017

+1

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

No branches or pull requests

7 participants