Toggle Controls
Bare (chrome-free) boolean and choice controls: <oge-check-box> (two- or three-state), <oge-switch> (on/off with localized track text) and <oge-radio-group> (WAI-ARIA radiogroup with roving tabindex). All three share the select box expression vocabulary where items are involved and bind via [(value)], Signal Forms or reactive forms.
Check Box
A real (visually hidden) native checkbox drives semantics — label clicks, Space and aria-checked='mixed' come for free. value is boolean | null: null always renders the indeterminate dash, and threeState lets users cycle into it.
null (indeterminate)import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeCheckBox } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeCheckBox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-check-box [(value)]="agreed">I agree to the terms</oge-check-box>
<!-- tri-state: null renders the indeterminate dash;
threeState lets USERS cycle null → true → false → null -->
<oge-check-box
[threeState]="true"
text="Select all"
[(value)]="all"
/>
`,
})
export class Demo {
protected readonly agreed = signal<boolean | null>(false);
protected readonly all = signal<boolean | null>(null);
}Switch
A native <button role='switch'> with aria-checked and a sliding thumb. Track texts default to the localized switchOn/switchOff messages ('ON'/'OFF'); override per instance or pass empty strings to hide them. The reference swipe gesture is deliberately not replicated.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeSwitch } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeSwitch],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- label feeds aria-label — always name your switch -->
<oge-switch label="Notifications" [(value)]="notify" />
<!-- track texts come from the localized messages (ON/OFF);
override per instance, empty string hides them -->
<oge-switch label="Localized" onText="AÇIK" offText="KAPALI" [(value)]="enabled" />
<oge-switch label="Plain" onText="" offText="" [(value)]="plain" />
`,
})
export class Demo {
protected readonly notify = signal(true);
protected readonly enabled = signal(false);
protected readonly plain = signal(false);
}Radio Group
Flat items with displayExpr/valueExpr/disabledExpr; layout switches column/row. Arrows move focus and selection (wrapping, disabled skipped, RTL-aware) per the WAI-ARIA radio-group pattern.
teamimport { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { OgeRadioGroup } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [OgeRadioGroup],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-radio-group
label="Plan"
[items]="plans"
displayExpr="name"
valueExpr="id"
disabledExpr="soldOut"
[(value)]="planId"
/>
<oge-radio-group
label="Priority"
layout="horizontal"
[items]="['Low', 'Normal', 'High']"
[(value)]="priority"
/>
`,
})
export class Demo {
protected readonly plans = [
{ id: 'free', name: 'Free', soldOut: false },
{ id: 'pro', name: 'Pro', soldOut: false },
{ id: 'enterprise', name: 'Enterprise', soldOut: true },
];
protected readonly planId = signal<unknown>('free');
protected readonly priority = signal<unknown>('Normal');
}Forms integration
All three implement Signal Forms' FormValueControl and the classic CVA (constructor-assignment pattern) — [formField], formControl and ngModel all work. valueCommitted carries previousValue and the originating event.
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { FormField, form } from '@angular/forms/signals';
import { OgeCheckBox, OgeRadioGroup, OgeSwitch } from '@oge-ui/inputs';
@Component({
selector: 'demo-root',
imports: [ReactiveFormsModule, FormField, OgeCheckBox, OgeRadioGroup, OgeSwitch],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- Signal Forms — auto-binds errors/touched/disabled -->
<oge-check-box [formField]="f.terms">Accept terms</oge-check-box>
<oge-switch label="Marketing" [formField]="f.marketing" />
<oge-radio-group [items]="plans" valueExpr="id" [formField]="f.plan" />
<!-- reactive forms work unchanged -->
<oge-check-box [formControl]="termsCtrl" text="Accept terms" />
`,
})
export class Demo {
protected readonly plans = [
{ id: 'free', name: 'Free' },
{ id: 'pro', name: 'Pro' },
];
protected readonly model = signal({
terms: false,
marketing: false,
plan: 'free',
});
protected readonly f = form(this.model);
protected readonly termsCtrl = new FormControl(false, { nonNullable: true });
}Keyboard & accessibility
- Check box: native semantics — Space toggles, label clicks work,
aria-checked="mixed"via the nativeindeterminateproperty. - Switch: Space/Enter toggle the
role="switch"button;labelfeedsaria-label. - Radio group: one tab stop (roving tabindex); ↓/→/↑/← move focus and selection with wrap-around, Home/End jump to the edges, disabled items are skipped.