Selected-indicator alignment pattern
Menu checkbox and radio items line their labels up only when every row reserves the checkmark's width, checked or not. [forMenuItemIndicator] is the one selection indicator that still keeps a [forceMount] opt-in for exactly that, and hiding the glyph with opacity rather than display: none keeps the slot it reserves.
Select (
[forSelectIndicator]) and Combobox ([forComboboxIndicator]) no longer expose[forceMount]: their indicators always self-hide while unselected (inlinedisplay:none+hidden). For column alignment in those primitives, render a fixed-width spacer in the option layout (e.g. a CSS grid column or a sibling element) rather than keeping the indicator mounted.
The problem
By default, an indicator that belongs to an unchecked item gets the hidden attribute plus an inline display: none, which collapses its layout box to nothing. Rows that are checked therefore reserve width for the checkmark while unchecked rows do not, causing item labels to misalign row-to-row.
The pattern: [forceMount] + opacity
Set [forceMount]="true" on the indicator so it stays mounted and reserves its slot in every row regardless of state. Then hide the glyph visually, not structurally, with a CSS rule keyed on the data-state attribute the directive reflects:
<!-- Inside [forMenuCheckboxItem] -->
<button forMenuCheckboxItem [(checked)]="bold" class="menu-item">
<span forMenuItemIndicator [forceMount]="true" class="indicator">✓</span>
Bold
</button>
/* Hide the glyph when the item is not checked, but keep its layout slot. */
.indicator[data-state='unchecked'] {
opacity: 0;
}
Exit animation
[forceMount]="true" is also the prerequisite for wrapping the glyph in an animate.leave exit animation, because Angular requires the element to stay in the DOM for the duration of the animation:
<span forMenuItemIndicator [forceMount]="true" class="indicator" animate.leave="fade-out">✓</span>
Without [forceMount], the indicator is unmounted immediately when the item is unchecked and no exit animation runs.