Logo

Switch

Native checkbox-based toggle control for boolean settings.

Preview

Usage

TS

import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { FrSwitchModule } from '@frame-ui-ng/components/switch';

notificationsControl = new FormControl(true, { nonNullable: true });

HTML

<label frSwitchField>
  <input frSwitch type="checkbox" [formControl]="notificationsControl" />

  <span frSwitchContent>
    <span frSwitchLabel>Enable release notifications</span>
    <span frSwitchDescription>Notify workspace members when a rollout completes.</span>
  </span>
</label>

Examples

Basic switch

Use a switch for immediate on/off settings where the current state should be visible without opening another surface.

Sizes

Use the default size for settings pages and the small size when switches sit inside denser toolbars or filters.

Disabled state

Disable a switch when a policy or upstream dependency temporarily prevents changes while still exposing the current state.

Invalid with reactive forms

Reactive forms can drive required confirmation flows directly, so the switch track and error text stay synchronized with Angular validation.

Custom Styling

Scope switch token overrides to a wrapper when a settings area needs different spacing, a larger track, or a more pronounced checked state without changing the primitive structure.

Token Inspector

Hover the field wrapper, switch control, content stack, label, description, or error copy to inspect the tokens that govern sizing, spacing, checked state, and validation treatment.

Design Tokens

Use these CSS custom properties to tune switch sizing, checked-state surfaces, thumb treatment, field spacing, and helper-text typography without changing the primitive structure.

SCSS


  --frame-switch-field-gap: 0.75rem;
  --frame-switch-field-color: var(--frame-foreground);
  --frame-switch-field-disabled-color: var(--frame-muted-foreground);
  --frame-switch-content-gap: 0.25rem;
  --frame-switch-label-font-size: 0.875rem;
  --frame-switch-label-font-weight: 600;
  --frame-switch-description-color: var(--frame-muted-foreground);
  --frame-switch-description-font-size: 0.8125rem;
  --frame-switch-error-color: var(--frame-destructive);
  --frame-switch-error-font-size: 0.8125rem;
  --frame-switch-width: 2.25rem;
  --frame-switch-height: 1.25rem;
  --frame-switch-thumb-size: 1rem;
  --frame-switch-padding: 0.125rem;
  --frame-switch-sm-width: 1.75rem;
  --frame-switch-sm-height: 1rem;
  --frame-switch-sm-thumb-size: 0.75rem;
  --frame-switch-radius: 999px;
  --frame-switch-bg: var(--frame-input);
  --frame-switch-hover-bg: color-mix(in srgb, var(--frame-input) 82%, var(--frame-foreground));
  --frame-switch-checked-bg: var(--frame-primary);
  --frame-switch-checked-hover-bg: color-mix(in srgb, var(--frame-primary) 88%, var(--frame-foreground));
  --frame-switch-thumb-bg: var(--frame-background);
  --frame-switch-thumb-shadow: 0 1px 2px rgb(0 0 0 / 0.22);
  --frame-switch-border-shadow: inset 0 0 0 1px color-mix(in srgb, var(--frame-border) 70%, transparent);
  --frame-switch-checked-border-shadow: inset 0 0 0 1px color-mix(in srgb, var(--frame-primary) 80%, var(--frame-border));
  --frame-switch-focus-shadow: 0 0 0 3px color-mix(in srgb, var(--frame-ring) 28%, transparent);
  --frame-switch-invalid-border: color-mix(in srgb, var(--frame-destructive) 65%, var(--frame-border));
  --frame-switch-invalid-shadow: inset 0 0 0 1px color-mix(in srgb, var(--frame-destructive) 40%, transparent), 0 0 0 3px color-mix(in srgb, var(--frame-destructive) 14%, transparent);
  --frame-switch-disabled-opacity: 0.5;
  --frame-switch-transition-duration: 160ms;
  --frame-switch-transition-easing: cubic-bezier(0.16, 1, 0.3, 1);