Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions core/src/components/checkbox/checkbox.scss
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@
@include margin(null, null, $checkbox-item-label-margin-bottom, null);
}

/**
* The native control it must be hidden with display:none instead of aria-hidden="true"
* to avoid nested interactive elements accessibility issues
*/
.checkbox-native-control {
display: none;
}

/**
* If no label text is placed into the slot
* then the element should be hidden otherwise
Expand Down
26 changes: 24 additions & 2 deletions core/src/components/checkbox/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ export class Checkbox implements ComponentInterface {
this.ionBlur.emit();
};

private onKeyDown = (ev: KeyboardEvent) => {
if (ev.key === ' ' || ev.key === 'Enter') {
ev.preventDefault();
if (!this.disabled) {
this.toggleChecked(ev);
}
}
};

private onClick = (ev: MouseEvent) => {
if (this.disabled) {
return;
Expand Down Expand Up @@ -253,11 +262,22 @@ export class Checkbox implements ComponentInterface {

renderHiddenInput(true, el, name, checked ? value : '', disabled);

// Determine appropriate accessible name
const hasLabelContent = el.textContent !== '';
const inputLabelId = inputId + '-lbl';

// The host must be a checkbox role to support Safari's Voice Over accessibility
return (
<Host
role="checkbox"
aria-checked={indeterminate ? 'mixed' : `${checked}`}
aria-describedby={this.getHintTextID()}
aria-invalid={this.getHintTextID() === this.errorTextId}
aria-labelledby={hasLabelContent ? inputLabelId : null}
aria-label={!hasLabelContent ? inheritedAttributes['aria-label'] || 'checkbox' : null}
aria-disabled={disabled ? 'true' : null}
tabindex="0"
onKeyDown={this.onKeyDown}
class={createColorClasses(color, {
[mode]: true,
'in-item': hostContext('ion-item', el),
Expand All @@ -271,13 +291,14 @@ export class Checkbox implements ComponentInterface {
})}
onClick={this.onClick}
>
<label class="checkbox-wrapper">
<label class="checkbox-wrapper" htmlFor={inputId}>
{/*
The native control must be rendered
before the visible label text due to https://bugs.webkit.org/show_bug.cgi?id=251951
*/}
<input
type="checkbox"
class="checkbox-native-control"
checked={checked ? true : undefined}
disabled={disabled}
id={inputId}
Expand All @@ -291,9 +312,10 @@ export class Checkbox implements ComponentInterface {
<div
class={{
'label-text-wrapper': true,
'label-text-wrapper-hidden': el.textContent === '',
'label-text-wrapper-hidden': !hasLabelContent,
}}
part="label"
id={inputLabelId}
>
<slot></slot>
{this.renderHintText()}
Expand Down
Loading