Skip to content
Components, switch product

Toggle

<gui-toggle> is an on and off switch, for a setting that takes effect at once. It renders a native checkbox with the switch role, styled as a track and a knob.

Code of the example above
ToggleBasic.tsx
import { GuiToggle } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function ToggleBasic() {
return <GuiToggle label="Dark mode" />;
}

The element’s entry point is @golemui/gui-components/toggle, which registers <gui-toggle> and nothing else. To install the package and load the stylesheet, see Installation.

The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.

DarkModeField.tsx
import { useState } from 'react';
import { GuiToggle } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function DarkModeField() {
const [darkMode, setDarkMode] = useState(false);
return (
<GuiToggle
label="Dark mode"
name="darkMode"
value={darkMode}
onGuiChange={(event) => setDarkMode(event.detail.value ?? false)}
/>
);
}

Properties

Property Type Default Description
localeId attribute locale-id string 'en' BCP 47 locale for formatting and parsing, such as en-US or es.
value boolean – Whether it is on. As an attribute, value turns it on and value="false" does not.
togglePosition attribute toggle-position 'left' | 'right' 'left' Side of the label the switch is on.
uid string – The id of the element's control. The ids of its label, hint and errors derive from it. Optional: when it is not set, the element generates one from its tag name and a page-wide counter, e.g. gui-textinput-3. Generated ids depend on creation order, so a server-rendered element must set uid or its ids will not match after hydration.

Form control properties

Every field has these, from the base class of the form controls.

Property Type Default Description
name string – The name the value is submitted under. Without it the element stays out of its form. Reflected, like a native control's: the form reads a single value's name from the attribute.
label string – The visible label, which also names the control for assistive technology. Without one the control has no accessible name; the date, time, range, tags and file fields warn about it in development builds.
hint string – Help text shown under the label and announced as the control's description.
errors string[] [] Error messages to show, typically from your own validation. They show as soon as they are set, unless touched is false.
touched boolean – Whether the user has interacted with the control. Leave it unset unless you validate on interaction: false holds errors back until it becomes true.
required boolean false The control needs a value: it is marked as required and, with a name, blocks submission.
readOnly attribute readonly boolean – The value can be read and focused but not changed.
disabled boolean – Disabled by its own attribute or by a disabled <fieldset> around it. Setting the property sets the attribute, like a native control's.
form read-only, no attribute HTMLFormElement | null – The <form> the element belongs to, if any.
validity read-only, no attribute ValidityState – The element's validity, like a native control's.
validationMessage read-only, no attribute string – The message the browser shows for an invalid value.

Methods

Method Description
checkValidity(): boolean –
reportValidity(): boolean –

Events

Every event bubbles and is composed, so you can listen on any ancestor.

Event Description
gui-input The user changed the value. detail.value is the new value.
gui-change The user committed the value. detail.value is the committed value.
gui-blur Focus left the control.

CSS custom properties

Property Default Description
--gui-toggle-width – Width of the switch.
--gui-toggle-height – Height of the switch.
--gui-toggle-slider-width – Width of the knob.
--gui-toggle-slider-height – Height of the knob.
--gui-toggle-slider-transform – Distance the knob moves when on.

The value is true or false. As an attribute, value turns the switch on and value="false" leaves it off. From JavaScript, set the value property to true or false.

Code of the example above
ToggleOn.tsx
import { GuiToggle } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function ToggleOn() {
return <GuiToggle label="Automatic updates" value />;
}

hint shows under the label. Screen readers read it as the switch’s description.

Code of the example above
ToggleHint.tsx
import { GuiToggle } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function ToggleHint() {
return (
<GuiToggle
label="Vacation reply"
hint="Replies to every email with your away message."
/>
);
}

The switch sits before the label. toggle-position="right" puts it after the label.

Code of the example above
TogglePosition.tsx
import { GuiToggle } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TogglePosition() {
return (
<GuiToggle label="Show weekends in the calendar" togglePosition="right" />
);
}

A disabled switch can’t be focused or changed, and a form doesn’t submit it. A readonly switch can be focused but clicks and Space don’t change it, and a form submits its value.

Code of the example above
ToggleDisabledReadonly.tsx
import { GuiToggle } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function ToggleDisabledReadonly() {
return (
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell">
<GuiToggle label="Backups" value disabled />
</div>
<div className="gui-grid__cell">
<GuiToggle label="Single sign-on" value readOnly />
</div>
</div>
);
}

A switch usually applies its setting right away. Listen to gui-change: event.detail.value is the new state.

Notifications are off.

Code of the example above
ToggleEvents.tsx
import { useState } from 'react';
import { GuiToggle } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function ToggleEvents() {
const [on, setOn] = useState(false);
return (
<>
<GuiToggle
id="alerts-toggle"
label="Push notifications"
onGuiChange={(event) => setOn(!!event.detail.value)}
/>
<p id="alerts-status" role="status">
Notifications are {on ? 'on' : 'off'}.
</p>
</>
);
}

Five tokens size the switch: the track’s width and height, the knob’s width and height, and how far the knob travels when the switch turns on. Set them together on the switch, or on any element around it.

Code of the example above
ToggleSize.tsx
import { GuiToggle } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
const sizeStyles = `
.large-toggle {
--gui-toggle-width: 52px;
--gui-toggle-height: 28px;
--gui-toggle-slider-width: 24px;
--gui-toggle-slider-height: 24px;
--gui-toggle-slider-transform: 24px;
}
`;
export function ToggleSize() {
return (
<>
<GuiToggle className="large-toggle" label="Focus mode" value />
<style>{sizeStyles}</style>
</>
);
}

With a name, a <gui-toggle> that is on submits on under that name, like a native checkbox, and one that is off submits nothing. required blocks the submit while the switch is off. Submit the form below, then change the switches and submit again.

Code of the example above
ToggleNativeForm.tsx
import { useState, type SubmitEvent } from 'react';
import { GuiButton, GuiToggle } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function ToggleNativeForm() {
const [publicProfile, setPublicProfile] = useState<boolean | undefined>(true);
const [result, setResult] = useState('');
const onSubmit = (event: SubmitEvent<HTMLFormElement>) => {
event.preventDefault();
const data = Object.fromEntries(new FormData(event.currentTarget));
setResult(`Submitted ${JSON.stringify(data)}`);
};
return (
<form id="privacy" onSubmit={onSubmit}>
<div className="gui-grid">
<div className="gui-grid__cell">
<GuiToggle
label="Public profile"
name="publicProfile"
value={publicProfile}
onGuiChange={(event) => setPublicProfile(event.detail.value)}
/>
</div>
<div className="gui-grid__cell">
<GuiToggle label="Show my email" name="showEmail" />
</div>
<div className="gui-grid__cell">
<GuiButton type="submit" label="Save settings" />
</div>
</div>
<p id="privacy-result" role="status">
{result}
</p>
</form>
);
}

How the elements submit, validate and reset is in Native forms.

<gui-toggle> renders into the light DOM, so you can style every part with plain CSS. The label wraps the switch and its text, and the hint and the errors follow it:

gui-toggle
<gui-toggle class="gui-field">
<label class="gui-label" for="vacation" id="vacation_label">
<div class="gui-widget gui-widget--horizontal gui-toggle--switch">
<input
type="checkbox"
role="switch"
id="vacation"
aria-describedby="vacation_hint"
aria-invalid="true"
aria-errormessage="vacation_errors"
aria-required="true"
/>
<span class="gui-toggle--slider" role="presentation"></span>
</div>
<span class="gui-label__container">
<span class="gui-label__text">
Vacation reply<span aria-hidden="true"> *</span>
</span>
</span>
</label>
<div class="gui-widget-hint">
<span id="vacation_hint">Replies with your away message.</span>
<div class="gui-validator" id="vacation_errors" role="alert">
<div class="gui-validator__error">Set an away message first.</div>
</div>
</div>
</gui-toggle>
ClassPart
gui-fieldThe element itself. Lines its parts up with the fields next to it in a gui-grid row.
gui-toggle--rightOn the element with toggle-position="right".
gui-labelThe label, with the switch and the text.
gui-widgetThe box around the switch.
gui-widget--horizontalOn the box: lays it out in a row.
gui-toggle--switchOn the box: the switch, sized by the toggle tokens. The checkbox in it is transparent.
gui-toggle--sliderThe track, with the knob drawn as its ::before.
gui-label__containerThe wrapper of the label text.
gui-label__textThe label text and the required marker.
gui-widget-hintThe hint, and the errors after it. It is always there; the hint text has the hint’s id.
gui-validatorThe errors container. It is always there, so the alert role announces new errors.
gui-validator__errorOne error message.

The ids derive from uid, or from a generated id such as gui-toggle-3 when you don’t set one. Select by class or by tag, not by id.

The switch reads these design tokens. Set them on :root to change every switch, or on any element to change the switches inside it:

TokenWhat it styles
--gui-toggle-widthThe track’s width.
--gui-toggle-heightThe track’s height.
--gui-toggle-slider-widthThe knob’s width.
--gui-toggle-slider-heightThe knob’s height.
--gui-toggle-slider-transformHow far the knob moves when the switch turns on.
--gui-bg-defaultThe track while off, and the knob while on.
--gui-border-defaultThe track’s border, and the knob while off.
--gui-intent-primaryThe track and its border while on.
--gui-bg-disabledThe track when disabled.
--gui-text-defaultThe label text.
--gui-font-familyThe font.
--gui-font-smThe text size of the label.
--gui-font-xsThe text size of the hint and the errors.
--gui-text-secondaryThe hint.
--gui-border-focusThe track’s border while focused.
--gui-shadow-focusThe focus ring.
--gui-intent-errorThe track’s border and the messages when there are errors.
--gui-shadow-focus-errorThe focus ring when there are errors.
--gui-space-1The space above the errors.
styles.css
/* A brand color for every switch that is on. */
:root {
--gui-intent-primary: #2f7d4f;
}

--gui-intent-primary also colors buttons, ticks and selections. To recolor only the switches, override the rule instead:

styles.css
gui-toggle input:checked + .gui-toggle--slider {
background-color: #2f7d4f;
border-color: #2f7d4f;
}

To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.

  • Role. The switch is a native checkbox with role="switch", so screen readers announce it as a switch that is on or off.
  • Name. The label names the switch. The native <label> wraps it, so a click on the text flips it too. Without a label the switch has no accessible name.
  • Description. The hint is the switch’s description, through aria-describedby.
  • Errors. While errors show, the switch has aria-invalid="true" and points at them with aria-errormessage, and screen readers announce new errors as they appear.
  • Required and read-only. required sets aria-required and readonly sets aria-readonly. A read-only switch stays in the tab order.
KeyAction
TabMoves focus to the switch, and away from it.
SpaceTurns it on or off, and fires gui-change.

What every element shares, such as translating the browser messages, is in Accessibility.