diff --git a/src/material/checkbox/checkbox.html b/src/material/checkbox/checkbox.html
index a5101aa584c8..1fc2bd41dffb 100644
--- a/src/material/checkbox/checkbox.html
+++ b/src/material/checkbox/checkbox.html
@@ -50,8 +50,13 @@
Avoid putting a click handler on the to fix duplicate navigation stop on Talk Back
(#14385). Putting a click handler on the caused this bug because the browser produced
an unnecessary accessibility tree node.
+
+ The is omitted entirely when no label content is projected so accessibility tooling
+ does not flag an empty in the DOM (see #33230).
-->
-
+ @if (_hasLabel()) {
+
+ }
diff --git a/src/material/checkbox/checkbox.spec.ts b/src/material/checkbox/checkbox.spec.ts
index 65219cef98ea..dc144d6026c1 100644
--- a/src/material/checkbox/checkbox.spec.ts
+++ b/src/material/checkbox/checkbox.spec.ts
@@ -1123,6 +1123,27 @@ describe('MatCheckbox', () => {
fixture.detectChanges();
expect(checkboxInnerContainer.querySelector('input')!.hasAttribute('value')).toBe(false);
});
+
+ it('should not render an empty label element when no label is provided', () => {
+ // Run change detection twice: the first pass instantiates the view and projects the
+ // (empty) content, the second flushes the signal update from `ngAfterContentInit` that
+ // hides the now-empty `` element.
+ fixture.detectChanges();
+ fixture.detectChanges();
+ expect(checkboxInnerContainer.querySelector('label')).toBeNull();
+ });
+
+ it('should render a label element when label content is provided', () => {
+ const labeledFixture = TestBed.createComponent(CheckboxWithoutLabel);
+ labeledFixture.componentInstance.label = 'Has a label';
+ labeledFixture.detectChanges();
+
+ const innerContainer = labeledFixture.debugElement
+ .query(By.directive(MatCheckbox))!
+ .query(By.css('.mdc-form-field'))!.nativeElement as HTMLElement;
+ expect(innerContainer.querySelector('label')).not.toBeNull();
+ expect(innerContainer.querySelector('label')!.textContent!.trim()).toBe('Has a label');
+ });
});
});
diff --git a/src/material/checkbox/checkbox.ts b/src/material/checkbox/checkbox.ts
index 67f5a30f700a..37e75a94977c 100644
--- a/src/material/checkbox/checkbox.ts
+++ b/src/material/checkbox/checkbox.ts
@@ -8,6 +8,7 @@
import {_IdGenerator, FocusableOption} from '@angular/cdk/a11y';
import {
+ AfterContentInit,
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
@@ -108,7 +109,13 @@ export class MatCheckboxChange {
imports: [MatRipple, _MatInternalFormField],
})
export class MatCheckbox
- implements AfterViewInit, OnChanges, ControlValueAccessor, Validator, FocusableOption
+ implements
+ AfterContentInit,
+ AfterViewInit,
+ OnChanges,
+ ControlValueAccessor,
+ Validator,
+ FocusableOption
{
_elementRef = inject>(ElementRef);
private _changeDetectorRef = inject(ChangeDetectorRef);
@@ -207,8 +214,15 @@ export class MatCheckbox
/** The native `` element */
@ViewChild('input') _inputElement!: ElementRef;
- /** The native `