Getting StartedInstallation and setup guides 5
LayoutWorkflow and structural layout components 7
OverlayModal and floating layer surfaces 3
FeedbackStatus, empty, progress, and loading placeholder patterns 5
FormInput and selection components 17
UtilityGeneral-purpose interface utilities 7
NavigationMenu surfaces and hierarchical actions 7

API reference

The component layer keeps the primitive pressed-button semantics. The underlying rendered button still exposes the same aria-pressed, data-state, and focus hooks that power the headless directive.

Primitive bridge

html
<button tngToggle [pressed]="isPinned()" (pressedChange)="isPinned.set($event)">
  Pin sidebar
</button>
HookAppears onPurpose
aria-pressedRendered buttonAnnounces the pressed state for assistive technologies.
data-state="on|off"Rendered buttonPrimary styling hook for selected and idle states.
data-disabledRendered buttonApplied when the toggle or its parent group is disabled.
data-focused, data-focus-visibleRendered buttonLets component themes express focus and keyboard focus separately.
data-slot="toggle"Rendered buttonStable selector for tests and internal wrapper styling.

<tng-toggle> and <tng-toggle-group>

<tng-toggle> is the styled, icon-slot wrapper for the primitive directive. Add <tng-toggle-group> when the selected value should be coordinated across multiple toggles.

Standalone component usage

html
<tng-toggle
  [pressed]="isPinned()"
  pressedLabel="Unpin sidebar"
  unpressedLabel="Pin sidebar"
  (pressedChange)="isPinned.set($event)"
>
  <span offIcon>P</span>
  <span onIcon>P</span>
</tng-toggle>

Grouped selection usage

html
<tng-toggle-group
  selectionMode="single"
  ariaLabel="Editor density"
  [value]="density()"
  (valueChange)="onDensityChange($event)"
>
  <tng-toggle
    [value]="'compact'"
    pressedLabel="Compact density selected"
    unpressedLabel="Select compact density"
  >
    <span offIcon>C</span>
    <span onIcon>C</span>
  </tng-toggle>
  <tng-toggle
    [value]="'comfortable'"
    pressedLabel="Comfortable density selected"
    unpressedLabel="Select comfortable density"
  >
    <span offIcon>M</span>
    <span onIcon>M</span>
  </tng-toggle>
</tng-toggle-group>

<tng-toggle> component contract

Input / OutputTypeDefaultNotes
valuestring | nullnullItem identity inside <tng-toggle-group>. Set this for grouped selection.
pressedboolean | nullnullControlled pressed state for standalone usage.
defaultPressedbooleanfalseInitial value for uncontrolled standalone toggles.
disabledbooleanfalseDisables the item directly. Group-level disabled also flows through.
pressedLabelstring'Enabled'Fallback aria-label when the toggle is pressed.
unpressedLabelstring'Disabled'Fallback aria-label when the toggle is not pressed.
ariaLabel, ariaLabelledby, ariaDescribedBystring | nullnullExplicit accessibility hooks. ariaLabel overrides the pressed/unpressed labels.
pressedChangebooleanOutputEmits on user activation and underlies ControlValueAccessor updates.
[offIcon], [onIcon]Projected slotsOptionalThe component renders the matching slot based on the current state.

<tng-toggle-group> component contract

Input / OutputTypeDefaultNotes
selectionMode'multiple' | 'single''multiple'Choose one value with value, or several with values.
orientation'horizontal' | 'vertical''horizontal'Reflected to data-orientation for styling and testing.
value, defaultValue, valueChangestring | nullnullSingle-selection contract.
values, defaultValues, valuesChangereadonly string[][]Multiple-selection contract.
disabledbooleanfalseDisables every nested toggle and applies group-level disabled hooks.
ariaLabel, ariaLabelledbystring | nullnullAccessible name for the selection region.

Reactive forms with standalone toggle

ts
readonly pinSidebarControl = new FormControl(false, { nonNullable: true });

<tng-toggle formControlName="pinSidebar">
  <span offIcon>P</span>
  <span onIcon>P</span>
</tng-toggle>

Angular Signal Forms can bind <tng-toggle> directly with [formField] for standalone boolean fields. Keep <tng-toggle-group> on its existing controlled value/values contract for now.

Signal forms with standalone toggle

ts
import { Component, signal } from '@angular/core';
import { FormField, form } from '@angular/forms/signals';
import { TngToggleComponent } from '@tailng-ui/components';

@Component({
  selector: 'app-pin-sidebar-signal-form',
  standalone: true,
  imports: [FormField, TngToggleComponent],
  template: \`
    <tng-toggle
      pressedLabel="Unpin sidebar"
      unpressedLabel="Pin sidebar"
      [formField]="preferencesForm.pinSidebar"
    >
      <span offIcon>P</span>
      <span onIcon>P</span>
    </tng-toggle>
  \`,
})
export class PinSidebarSignalFormComponent {
  readonly preferencesModel = signal({ pinSidebar: false });
  readonly preferencesForm = form(this.preferencesModel);
}