# Checkbox

A box with three states, the third of which Slint has no property for.

```slint
import { Checkbox } from "@glint/components/checkbox.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> agreed: false;

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

        HorizontalLayout {
            alignment: center;

            Checkbox {
                label: "Accept terms and conditions";
                description: "You agree to our Terms of Service and Privacy Policy.";
                checked <=> root.agreed;
            }
        }

        Text {
            text: root.agreed ? "Status: Agreed" : "Status: Not agreed";
            color: Tokens.color-muted-foreground;
            font-size: Tokens.typography-body-sm-size;
            horizontal-alignment: center;
        }
    }
}
```

## Usage

```slint
import { Checkbox } from "@glint/components/checkbox.slint";

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

    VerticalLayout {
        alignment: center;

        Checkbox {
            label: "Receive newsletter";
            checked <=> root.send-updates;
            toggled(is-checked) => {
                // handle state change
            }
        }
    }
}
```

`Checkbox` renders an interactive square box with support for checked, unchecked, and indeterminate states. Clicking the box or its label toggles `checked` and fires the `toggled(bool)` callback.

## Examples

### Tri-state indeterminate

`indeterminate` expresses a mixed state where some child options are selected but not all. The mixed state outranks `checked` visually (displaying a dash icon) and announces as “Partially checked” to assistive technology. Activating a mixed checkbox resolves it directly to checked.

```slint
import { Checkbox } from "@glint/components/checkbox.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> item-a: true;
    in-out property <bool> item-b: false;

    property <bool> all-checked: root.item-a && root.item-b;
    property <bool> some-checked: (root.item-a || root.item-b) && !root.all-checked;

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

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                spacing: 10px;

                Checkbox {
                    label: "Select all notifications";
                    checked: root.all-checked;
                    indeterminate: root.some-checked;
                    toggled(checked) => {
                        root.item-a = checked;
                        root.item-b = checked;
                    }
                }

                HorizontalLayout {
                    padding-left: 24px;

                    VerticalLayout {
                        spacing: 8px;

                        Checkbox {
                            label: "Email digests";
                            checked <=> root.item-a;
                        }

                        Checkbox {
                            label: "Push notifications";
                            checked <=> root.item-b;
                        }
                    }
                }
            }
        }
    }
}
```

### Label and description

Provide `label` for primary caption text and `description` for supplementary consent or explanation text. Both texts align in a dedicated column beside the box.

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

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

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            Checkbox {
                label: "Enable auto-renew";
                description: "Your subscription will automatically renew at the end of each billing cycle.";
                checked: true;
            }
        }
    }
}
```

### Disabled and invalid states

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

```slint
import { Checkbox } from "@glint/components/checkbox.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;
            spacing: 24px;

            Checkbox {
                label: "Required consent";
                invalid: true;
                checked: false;
            }

            Checkbox {
                label: "Archived option";
                disabled: true;
                checked: true;
            }
        }
    }
}
```

## API Reference

### Properties

| Property        | Type          | Default    | Description                                                                                                                                                                                                                                                                              |
| --------------- | ------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `checked`       | `in-out bool` | `false`    | Two-way toggle state.                                                                                                                                                                                                                                                                    |
| `indeterminate` | `in bool`     | `false`    | The mixed state: neither checked nor unchecked. It outranks `checked` on screen and in the accessibility tree, so a select-all header can bind both and let "some" win when they disagree.                                                                                               |
| `label`         | `in string`   | no default | Text shown to the right of the box; clicking it also toggles.                                                                                                                                                                                                                            |
| `description`   | `in string`   | no default | Second line under the label, in the label's own column. The consent sentence a tick needs explaining, announced as the control's description rather than drawn as loose text under the row.                                                                                              |
| `disabled`      | `in bool`     | `false`    | When true, the checkbox dims and stops responding.                                                                                                                                                                                                                                       |
| `invalid`       | `in bool`     | `false`    | When true, the box 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 when the user toggles. |

### Functions

| Function                | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `focus-from-keyboard()` | Hand the box 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. |

## Accessibility

- **Checkbox role.** The component publishes an accessible `checkbox` role carrying `accessible-checkable: true` and `accessible-checked`.
- **The mixed state announces the words “Partially checked”.** Slint 1.17’s `accessible-checked` is a bool with no mixed value, so a mixed box reports `false` and puts the note on the same description channel `invalid` uses (ADR-0013). The two join rather than overwrite: a mixed box that is also invalid announces both.
- **`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.** `Tab` moves keyboard focus to the checkbox. `Space` or `Enter` toggles the state.
- **Focus ring.** Displays a 2px outer focus ring when focused via keyboard navigation.
