# Switch

A two-state toggle for a setting that takes effect as it is flipped.

```slint
import { Switch } from "@glint/components/switch.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 300px;
    background: Tokens.color-background;

    in-out property <bool> airplane-mode: false;

    VerticalLayout {
        alignment: center;
        spacing: 16px;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            Switch {
                label: "Airplane Mode";
                checked <=> root.airplane-mode;
            }
        }

        Text {
            text: root.airplane-mode ? "Status: Offline (Disconnected)" : "Status: Online (Connected)";
            color: Tokens.color-muted-foreground;
            font-size: Tokens.typography-body-sm-size;
            horizontal-alignment: center;
        }
    }
}
```

## Usage

```slint
import { Switch } from "@glint/components/switch.slint";

export component AppWindow inherits Window {
    in-out property <bool> notifications: true;

    VerticalLayout {
        alignment: center;

        Switch {
            label: "Push Notifications";
            checked <=> root.notifications;
            toggled(is-on) => {
                // handle toggle change
            }
        }
    }
}
```

`Switch` provides a sliding toggle control for turning individual binary options on or off.

## Examples

### Sizes

The `size` property adjusts the track, knob, and border radius proportionally:

- `SwitchSize.default`: Standard 36 × 20 px track.
- `SwitchSize.sm`: Compact 26 × 14 px track for dense toolbars or compact list items.

```slint
import { Switch, SwitchSize } from "@glint/components/switch.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 300px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        spacing: 16px;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                spacing: 16px;

                Switch {
                    size: SwitchSize.default;
                    label: "Default Switch";
                    checked: true;
                }

                Switch {
                    size: SwitchSize.sm;
                    label: "Compact Small Switch";
                    checked: true;
                }
            }
        }
    }
}
```

### Disabled and invalid states

Set `disabled: true` to dim the switch and prevent clicks. Set `invalid: true` to display a destructive error border and announce an invalid state.

```slint
import { Switch } from "@glint/components/switch.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 300px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        spacing: 16px;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                spacing: 14px;

                Switch {
                    label: "Required Consent Setting";
                    invalid: true;
                    checked: false;
                }

                Switch {
                    label: "Managed Enterprise Feature";
                    disabled: true;
                    checked: true;
                }
            }
        }
    }
}
```

## API Reference

### Properties

| Property        | Type            | Default              | Description                                                                                                                                                                                                                                                                              |
| --------------- | --------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `size`          | `in SwitchSize` | `SwitchSize.default` | Track + knob preset — sm or default.                                                                                                                                                                                                                                                     |
| `checked`       | `in-out bool`   | `false`              | Two-way on/off state.                                                                                                                                                                                                                                                                    |
| `label`         | `in string`     | no default           | Text shown to the right of the track.                                                                                                                                                                                                                                                    |
| `disabled`      | `in bool`       | `false`              | When true, the switch dims and stops responding.                                                                                                                                                                                                                                         |
| `invalid`       | `in bool`       | `false`              | When true, the track wears the destructive border and announces itself invalid. The message that explains the error belongs to the surrounding `Field` — bind this to that Field's `invalid`.                                                                                            |
| `focus-visible` | `out bool`      | no default           | Whether this control holds the keyboard \*and\* got it from the keyboard — the `focus-visible` a hover surface opens on. Published because Slint reports focus only to the element holding it, so a `Tooltip` wrapping this control cannot read it off the scope inside (tooltip.slint). |
| `focus-held`    | `out bool`      | no default           | The same focus, still true while a popup has borrowed the window's — what a hover surface opened by this control has to gate on, since showing itself is what takes `focus-visible` away. See `Tooltip`.                                                                                 |

### Callbacks

| Callback        | Description                         |
| --------------- | ----------------------------------- |
| `toggled(bool)` | Fired with the new `checked` value. |

### Functions

| Function                | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `focus-from-keyboard()` | Hand the control the keyboard the way a key press does, ring and all. A host that moves the focus onto a control because the user pressed something — `Questionnaire` stepping to the next question — cannot use `focus()`: Slint reports that as `programmatic`, which is how a host parking the keyboard looks, and the scope drops the ring for it. Published by every control that publishes `focus-visible`, for the same reason: the scope inside cannot be reached from outside the component. |

### Enums

| Enum         | Values          |
| ------------ | --------------- |
| `SwitchSize` | `sm`, `default` |

## Accessibility

- **Switch role.** Publishes `accessible-role: switch` with `accessible-checkable: true` and `accessible-checked`.
- **`invalid` announces the word “Invalid”.** Slint 1.17 publishes no `accessible-invalid`, so the state rides the description channel ADR-0013 opened: the control’s own description first, then `"Invalid"` after it. A red border is not the announcement — this is.
- **Keyboard navigation.** `Space` or `Enter` toggles state when focused.
- **Focus ring.** Displays a 2px outer ring when focused via keyboard navigation.
