API reference
Headless switch is a directive attached to button[tngSwitch]. It does not emit a custom Angular output or render a wrapper for you. Your app owns the thumb markup, the surrounding copy, and the state transitions.
Directive attachment
html
<button
tngSwitch
[checked]="releaseReady()"
ariaLabel="Release ready"
(click)="releaseReady.update(value => !value)"
>
<span class="switch-thumb" aria-hidden="true"></span>
</button>
Recommended labeled row
html
<div class="switch-row">
<button
tngSwitch
[checked]="notificationsEnabled()"
ariaLabel="Notifications"
(click)="notificationsEnabled.update(value => !value)"
>
<span class="switch-thumb" aria-hidden="true"></span>
</button>
<div class="switch-copy">
<span class="switch-copy__title">Notifications</span>
<span class="switch-copy__meta">Send release alerts to the on-call team.</span>
</div>
</div>
| Input | Type | Default | Notes |
|---|---|---|---|
checked | boolean | '' | string | false | Controls the ARIA state and the data-state hook. |
disabled | boolean | '' | string | false | Blocks interaction and reflects disabled plus data-disabled. |
required | boolean | '' | string | false | Reflects to aria-required for assistive technology. |
ariaLabel | string | null | null | Accessible name for the switch button when visible copy is separate. |
Reflected attributes
The directive reflects a minimal state contract onto the host button so your CSS can style checked, unchecked, and disabled states without extra wrapper logic.
| Attribute | When present | Purpose |
|---|---|---|
role="switch" | Always | Declares switch semantics on the button host. |
type="button" | Always | Prevents accidental form submission. |
aria-checked | Always | Mirrors the current on/off state as true or false. |
aria-required | When required | Signals mandatory state to assistive technology. |
data-state | Always | checked or unchecked styling hook. |
data-disabled | When disabled | Convenient disabled styling hook. |
data-slot="switch" | Always | Stable selector anchor for theme or utility CSS. |
Change handling
Headless switch does not own state updates. Listen to click and update the parent signal or store yourself.
switch-state.ts
ts
import { signal } from '@angular/core';
readonly notificationsEnabled = signal(false);
onNotificationsToggle(): void {
this.notificationsEnabled.update((value) => !value);
}
Because the primitive is a button, adjacent text is only visual. If the label copy sits outside the button, keep ariaLabel in sync with that visible text.