diff --git a/src/material/checkbox/checkbox.html b/src/material/checkbox/checkbox.html
index 9cac64e875b7..759f09f64bed 100644
--- a/src/material/checkbox/checkbox.html
+++ b/src/material/checkbox/checkbox.html
@@ -10,7 +10,10 @@
[attr.aria-labelledby]="ariaLabelledby"
[attr.aria-describedby]="ariaDescribedby"
[attr.aria-checked]="indeterminate ? 'mixed' : null"
+ [attr.aria-controls]="ariaControls"
[attr.aria-disabled]="disabled && disabledInteractive ? true : null"
+ [attr.aria-expanded]="ariaExpanded"
+ [attr.aria-owns]="ariaOwns"
[attr.name]="name"
[attr.value]="value"
[checked]="checked"
diff --git a/src/material/checkbox/checkbox.md b/src/material/checkbox/checkbox.md
index 0260fa3cf6dd..0cfb43e1bd12 100644
--- a/src/material/checkbox/checkbox.md
+++ b/src/material/checkbox/checkbox.md
@@ -74,3 +74,9 @@ binding these properties, as demonstrated below.
```
+
+Additionally, `MatCheckbox` now supports the following accessibility properties:
+
+- **`aria-expanded`**: Indicates whether the checkbox controls the visibility of another element. This should be a boolean value (`true` or `false`).
+- **`aria-controls`**: Specifies the ID of the element that the checkbox controls.
+- **`aria-owns`**: Specifies the ID of the element that the checkbox visually owns.
diff --git a/src/material/checkbox/checkbox.spec.ts b/src/material/checkbox/checkbox.spec.ts
index 9c029d2bc8e2..35fb4441d91e 100644
--- a/src/material/checkbox/checkbox.spec.ts
+++ b/src/material/checkbox/checkbox.spec.ts
@@ -807,6 +807,94 @@ describe('MatCheckbox', () => {
});
});
+ describe('with provided aria-expanded', () => {
+ let checkboxDebugElement: DebugElement;
+ let checkboxNativeElement: HTMLElement;
+ let inputElement: HTMLInputElement;
+
+ it('should use the provided postive aria-expanded', () => {
+ fixture = createComponent(CheckboxWithPositiveAriaExpanded);
+ checkboxDebugElement = fixture.debugElement.query(By.directive(MatCheckbox))!;
+ checkboxNativeElement = checkboxDebugElement.nativeElement;
+ inputElement = checkboxNativeElement.querySelector('input');
+
+ fixture.detectChanges();
+ expect(inputElement.getAttribute('aria-expanded')).toBe('true');
+ });
+
+ it('should use the provided negative aria-expanded', () => {
+ fixture = createComponent(CheckboxWithNegativeAriaExpanded);
+ checkboxDebugElement = fixture.debugElement.query(By.directive(MatCheckbox))!;
+ checkboxNativeElement = checkboxDebugElement.nativeElement;
+ inputElement = checkboxNativeElement.querySelector('input');
+
+ fixture.detectChanges();
+ expect(inputElement.getAttribute('aria-expanded')).toBe('false');
+ });
+
+ it('should not assign aria-expanded if none is provided', () => {
+ fixture = createComponent(SingleCheckbox);
+ checkboxDebugElement = fixture.debugElement.query(By.directive(MatCheckbox))!;
+ checkboxNativeElement = checkboxDebugElement.nativeElement;
+ inputElement = checkboxNativeElement.querySelector('input');
+
+ fixture.detectChanges();
+ expect(inputElement.getAttribute('aria-expanded')).toBe(null);
+ });
+ });
+
+ describe('with provided aria-controls', () => {
+ let checkboxDebugElement: DebugElement;
+ let checkboxNativeElement: HTMLElement;
+ let inputElement: HTMLInputElement;
+
+ it('should use the provided aria-controls', () => {
+ fixture = createComponent(CheckboxWithAriaControls);
+ checkboxDebugElement = fixture.debugElement.query(By.directive(MatCheckbox))!;
+ checkboxNativeElement = checkboxDebugElement.nativeElement;
+ inputElement = checkboxNativeElement.querySelector('input');
+
+ fixture.detectChanges();
+ expect(inputElement.getAttribute('aria-controls')).toBe('some-id');
+ });
+
+ it('should not assign aria-controls if none is provided', () => {
+ fixture = createComponent(SingleCheckbox);
+ checkboxDebugElement = fixture.debugElement.query(By.directive(MatCheckbox))!;
+ checkboxNativeElement = checkboxDebugElement.nativeElement;
+ inputElement = checkboxNativeElement.querySelector('input');
+
+ fixture.detectChanges();
+ expect(inputElement.getAttribute('aria-controls')).toBe(null);
+ });
+ });
+
+ describe('with provided aria-owns', () => {
+ let checkboxDebugElement: DebugElement;
+ let checkboxNativeElement: HTMLElement;
+ let inputElement: HTMLInputElement;
+
+ it('should use the provided aria-owns', () => {
+ fixture = createComponent(CheckboxWithAriaOwns);
+ checkboxDebugElement = fixture.debugElement.query(By.directive(MatCheckbox))!;
+ checkboxNativeElement = checkboxDebugElement.nativeElement;
+ inputElement = checkboxNativeElement.querySelector('input');
+
+ fixture.detectChanges();
+ expect(inputElement.getAttribute('aria-owns')).toBe('some-id');
+ });
+
+ it('should not assign aria-owns if none is provided', () => {
+ fixture = createComponent(SingleCheckbox);
+ checkboxDebugElement = fixture.debugElement.query(By.directive(MatCheckbox))!;
+ checkboxNativeElement = checkboxDebugElement.nativeElement;
+ inputElement = checkboxNativeElement.querySelector('input');
+
+ fixture.detectChanges();
+ expect(inputElement.getAttribute('aria-owns')).toBe(null);
+ });
+ });
+
describe('with provided tabIndex', () => {
let checkboxDebugElement: DebugElement;
let checkboxNativeElement: HTMLElement;
@@ -1237,6 +1325,38 @@ class CheckboxWithAriaLabelledby {}
})
class CheckboxWithAriaDescribedby {}
+/** Simple test component with an aria-expanded set with true. */
+@Component({
+ template: ``,
+ standalone: true,
+ imports: [MatCheckbox],
+})
+class CheckboxWithPositiveAriaExpanded {}
+
+/** Simple test component with an aria-expanded set with false. */
+@Component({
+ template: ``,
+ standalone: true,
+ imports: [MatCheckbox],
+})
+class CheckboxWithNegativeAriaExpanded {}
+
+/** Simple test component with an aria-controls set. */
+@Component({
+ template: ``,
+ standalone: true,
+ imports: [MatCheckbox],
+})
+class CheckboxWithAriaControls {}
+
+/** Simple test component with an aria-owns set. */
+@Component({
+ template: ``,
+ standalone: true,
+ imports: [MatCheckbox],
+})
+class CheckboxWithAriaOwns {}
+
/** Simple test component with name attribute */
@Component({
template: ``,
diff --git a/src/material/checkbox/checkbox.ts b/src/material/checkbox/checkbox.ts
index 19a09c881c29..b4627499dae5 100644
--- a/src/material/checkbox/checkbox.ts
+++ b/src/material/checkbox/checkbox.ts
@@ -160,6 +160,19 @@ export class MatCheckbox
/** The 'aria-describedby' attribute is read after the element's label and field type. */
@Input('aria-describedby') ariaDescribedby: string;
+ /**
+ * Users can specify the `aria-expanded` attribute which will be forwarded to the input element
+ */
+ @Input({alias: 'aria-expanded', transform: booleanAttribute}) ariaExpanded: boolean;
+
+ /**
+ * Users can specify the `aria-controls` attribute which will be forwarded to the input element
+ */
+ @Input('aria-controls') ariaControls: string;
+
+ /** Users can specify the `aria-owns` attribute which will be forwarded to the input element */
+ @Input('aria-owns') ariaOwns: string;
+
private _uniqueId: string;
/** A unique id for the checkbox input. If none is supplied, it will be auto-generated. */
diff --git a/tools/public_api_guard/material/checkbox.md b/tools/public_api_guard/material/checkbox.md
index bc59c890df69..e1b0acd3cf25 100644
--- a/tools/public_api_guard/material/checkbox.md
+++ b/tools/public_api_guard/material/checkbox.md
@@ -48,9 +48,12 @@ export class MatCheckbox implements AfterViewInit, OnChanges, ControlValueAccess
};
// (undocumented)
_animationMode?: string | undefined;
+ ariaControls: string;
ariaDescribedby: string;
+ ariaExpanded: boolean;
ariaLabel: string;
ariaLabelledby: string | null;
+ ariaOwns: string;
readonly change: EventEmitter;
get checked(): boolean;
set checked(value: boolean);
@@ -78,6 +81,8 @@ export class MatCheckbox implements AfterViewInit, OnChanges, ControlValueAccess
labelPosition: 'before' | 'after';
name: string | null;
// (undocumented)
+ static ngAcceptInputType_ariaExpanded: unknown;
+ // (undocumented)
static ngAcceptInputType_checked: unknown;
// (undocumented)
static ngAcceptInputType_disabled: unknown;
@@ -123,7 +128,7 @@ export class MatCheckbox implements AfterViewInit, OnChanges, ControlValueAccess
// (undocumented)
writeValue(value: any): void;
// (undocumented)
- static ɵcmp: i0.ɵɵComponentDeclaration;
+ static ɵcmp: i0.ɵɵComponentDeclaration;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration;
}