Toggle
A button that stays pressed or released, such as Bold in a text editor.
- v1.0.0
- WAI-ARIA: Button
- SSR
- Zoneless
- 1.3 kB gzip
import { Component } from '@angular/core';
import { NdIcon } from '@ndop/core';
import { Toggle } from '@/ui/toggle/toggle';
/** An icon-only toggle, named for screen readers. */
@Component({
selector: 'example-toggle-demo',
imports: [NdIcon, Toggle],
template: `
<button ndToggle aria-label="Toggle bold">
<svg ndIcon="bold"></svg>
</button>
`,
})
export class ToggleDemoExample {}
A toggle is a button that stays pressed or released, such as Bold in a
text editor. It's announced with its pressed state, comes in a default
and an outline style and three sizes, and binds with [(pressed)]. For
several related toggles, such as formatting or alignment, use a Toggle
Group, which adds arrow-key navigation.
Installation
npx @ndop/cli add togglepnpm dlx @ndop/cli add toggleyarn dlx @ndop/cli add togglebunx @ndop/cli add toggleInstall the dependencies
npm install @ndop/primitives @ndop/corepnpm add @ndop/primitives @ndop/coreyarn add @ndop/primitives @ndop/corebun add @ndop/primitives @ndop/coreCopy the code into your project
At the paths your
ndop.jsonaliases point to; these use the defaults.src/app/ui/toggle/toggle.tsimport { computed, Directive, input } from '@angular/core'; import { NdToggle } from '@ndop/primitives/toggle'; import { cn, variants, type VariantOf } from '@/lib/utils'; /** Class table for {@link Toggle}, shared with Toggle Group's items. */ export const toggleVariants = variants({ base: "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", variants: { variant: { default: 'bg-transparent', outline: 'border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground', }, size: { default: 'h-9 min-w-9 px-2', sm: 'h-8 min-w-8 px-1.5', lg: 'h-10 min-w-10 px-2.5', }, }, defaults: { variant: 'default', size: 'default' }, }); /** Visual style of a {@link Toggle}. */ export type ToggleVariant = VariantOf<typeof toggleVariants, 'variant'>; /** Size of a {@link Toggle}. */ export type ToggleSize = VariantOf<typeof toggleVariants, 'size'>; /** * A toggle button (`@ndop/primitives/toggle`): pressed or released, * announced with `aria-pressed`. Bind it with `[(pressed)]`. Keep its name * the same in both states, such as `aria-label="Toggle bold"`. * * @example * ```html * <button ndToggle aria-label="Toggle bold" [(pressed)]="bold"> * <svg ndIcon="bold"></svg> * </button> * ``` */ @Directive({ selector: 'button[ndToggle]', hostDirectives: [ { directive: NdToggle, inputs: ['pressed', 'defaultPressed', 'disabled'], outputs: ['pressedChange'], }, ], host: { 'data-slot': 'toggle', '[attr.data-variant]': 'variant()', '[attr.data-size]': 'size()', '[class]': 'classes()', }, }) export class Toggle { /** Visual style. */ readonly variant = input<ToggleVariant>('default'); /** Size. */ readonly size = input<ToggleSize>('default'); /** Extra classes, merged last so they win. */ readonly class = input(''); protected readonly classes = computed(() => cn( toggleVariants({ variant: this.variant(), size: this.size() }), this.class(), ), ); }src/app/lib/utils.ts/** * Class helpers used by every styled item. They come from `@ndop/core`; * this file is yours, so it is the place to customize them, for example by * wrapping `cn` with an extended tailwind-merge configuration for your own * utilities. * * @packageDocumentation */ export { cn, variants, type ClassValue, type VariantOf } from '@ndop/core';Update the import paths to match your project
The files import each other through the
@/uiand@/libaliases.
Usage
import { Toggle } from '@/ui/toggle/toggle';<button ndToggle aria-label="Toggle bold">
<svg ndIcon="bold"></svg>
</button>Examples
Toggle outline
The outline variant.
import { Component } from '@angular/core';
import { NdIcon } from '@ndop/core';
import { Toggle } from '@/ui/toggle/toggle';
/** The outline variant. */
@Component({
selector: 'example-toggle-outline',
imports: [NdIcon, Toggle],
template: `
<button ndToggle variant="outline" aria-label="Toggle italic">
<svg ndIcon="italic"></svg>
</button>
`,
})
export class ToggleOutlineExample {}
Toggle with text
A toggle with an icon and a label.
import { Component, signal } from '@angular/core';
import { NdIcon } from '@ndop/core';
import { Toggle } from '@/ui/toggle/toggle';
/**
* A toggle with an icon and a label, whose icon fills while it's pressed.
*/
@Component({
selector: 'example-toggle-with-text',
imports: [NdIcon, Toggle],
template: `
<button
ndToggle
variant="outline"
size="sm"
class="data-[state=on]:bg-transparent data-[state=on]:*:[svg]:fill-current"
[(pressed)]="saved"
>
<svg ndIcon="bookmark"></svg>
Bookmark
</button>
`,
})
export class ToggleWithTextExample {
protected readonly saved = signal(false);
}
Toggle sizes
The small, default and large sizes.
import { Component } from '@angular/core';
import { NdIcon } from '@ndop/core';
import { Toggle } from '@/ui/toggle/toggle';
/** The small, default and large sizes. */
@Component({
selector: 'example-toggle-sizes',
imports: [NdIcon, Toggle],
template: `
<div class="flex items-center gap-2">
<button ndToggle size="sm" aria-label="Toggle underline, small">
<svg ndIcon="underline"></svg>
</button>
<button ndToggle aria-label="Toggle underline">
<svg ndIcon="underline"></svg>
</button>
<button ndToggle size="lg" aria-label="Toggle underline, large">
<svg ndIcon="underline"></svg>
</button>
</div>
`,
})
export class ToggleSizesExample {}
Toggle disabled
A disabled toggle.
import { Component } from '@angular/core';
import { NdIcon } from '@ndop/core';
import { Toggle } from '@/ui/toggle/toggle';
/** A disabled toggle. */
@Component({
selector: 'example-toggle-disabled',
imports: [NdIcon, Toggle],
template: `
<button ndToggle disabled aria-label="Toggle underline">
<svg ndIcon="underline"></svg>
</button>
`,
})
export class ToggleDisabledExample {}
Accessibility
Implements the Button pattern of the WAI-ARIA Authoring Practices.
Keyboard
Keys the component handles.
| Key | Action |
|---|---|
| Space | Presses or releases it. |
| Enter | Presses or releases it. |
Focus
- Unchanged from a native button: activating it keeps focus on it.
Right-to-left
Nothing mirrors.
API reference
Toggle
button[ndToggle]A toggle button (@ndop/primitives/toggle): pressed or released,
announced with aria-pressed. Bind it with [(pressed)]. Keep its name
the same in both states, such as aria-label="Toggle bold".
Manages aria-pressed.
| Toggle binding | Type | Default |
|---|---|---|
[variant]Visual style. | 'default' | 'outline' | 'default' |
[size]Size. | 'default' | 'sm' | 'lg' | 'default' |
[class]Extra classes, merged last so they win. | string | '' |
[defaultPressed]Initial pressed when it isn't bound. | boolean | false |
[disabled]Disables it. A disabled bound field disables it too. | boolean | false |
[(pressed)]Whether it's pressed. Two-way bindable with [(pressed)]. | boolean | false |
| Data attribute | Values |
|---|---|
data-slot | toggle |
data-variant | — |
data-size | — |
data-state | — |
data-disabled | — |
Agent notes
Do
- Give an icon-only toggle an
aria-label, such as "Toggle bold". - Keep the same name in both states.
Don't
- Don't use a toggle for an on/off setting; use a Switch.
- Don't use it to trigger an action; use a Button.