# RangeSlider

Several stops on one track, each bounded by its neighbours.

```slint
import { RangeSlider } from "@glint/components/range-slider.slint";
import { Tokens } from "@glint/theme/tokens.slint";

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

    in-out property <[float]> price-range: [25, 75];

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

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 360px;

                RangeSlider {
                    minimum: 0;
                    maximum: 100;
                    step: 5;
                    values <=> root.price-range;
                    labels: ["Minimum Budget", "Maximum Budget"];
                }
            }
        }

        Text {
            text: "Range: $" + Math.round(root.price-range[0]) + " – $" + Math.round(root.price-range[1]);
            color: Tokens.color-muted-foreground;
            font-size: Tokens.typography-body-sm-size;
            horizontal-alignment: center;
        }
    }
}
```

## Usage

```slint
import { RangeSlider } from "@glint/components/range-slider.slint";

export component AppWindow inherits Window {
    in-out property <[float]> bounds: [10, 90];

    VerticalLayout {
        alignment: center;

        RangeSlider {
            minimum: 0;
            maximum: 100;
            step: 1;
            values <=> root.bounds;
            changed(thumb-index, new-val) => {
                // handle range adjustment
            }
        }
    }
}
```

`RangeSlider` allows users to select intervals or sub-ranges across two or more movable thumbs. Thumbs are clamped against their neighboring stops so they can meet but never cross.

## Examples

### Multi-stop values

`values` accepts two or more numbers. Providing three or more values creates a multi-stop slider with that number of draggable thumbs.

```slint
import { RangeSlider } from "@glint/components/range-slider.slint";
import { Tokens } from "@glint/theme/tokens.slint";

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

    in-out property <[float]> stops: [20, 50, 80];

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

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 360px;

                RangeSlider {
                    minimum: 0;
                    maximum: 100;
                    values <=> root.stops;
                    labels: ["Low Threshold", "Medium Threshold", "High Threshold"];
                }
            }
        }

        Text {
            text: "Stops: " + Math.round(root.stops[0]) + " / " + Math.round(root.stops[1]) + " / " + Math.round(root.stops[2]);
            color: Tokens.color-muted-foreground;
            font-size: Tokens.typography-body-sm-size;
            horizontal-alignment: center;
        }
    }
}
```

### Custom labels and stepping

Use `labels` to supply descriptive accessible names for each thumb in order, and `step` to define snapping increments.

```slint
import { RangeSlider } from "@glint/components/range-slider.slint";
import { Tokens } from "@glint/theme/tokens.slint";

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

    in-out property <[float]> hours: [9, 17];

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

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 360px;

                RangeSlider {
                    minimum: 0;
                    maximum: 24;
                    step: 1;
                    values <=> root.hours;
                    labels: ["Start Hour", "End Hour"];
                }
            }
        }

        Text {
            text: "Operating Hours: " + Math.round(root.hours[0]) + ":00 to " + Math.round(root.hours[1]) + ":00";
            color: Tokens.color-muted-foreground;
            font-size: Tokens.typography-body-sm-size;
            horizontal-alignment: center;
        }
    }
}
```

### Disabled state

Setting `disabled: true` dims the slider and disables drag, click, and keyboard interactions.

```slint
import { RangeSlider } from "@glint/components/range-slider.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;

            VerticalLayout {
                width: 360px;

                RangeSlider {
                    disabled: true;
                    values: [30, 70];
                }
            }
        }
    }
}
```

## API Reference

### Properties

| Property      | Type             | Default        | Description                                                                                                                                                                                                                                                                                                                |
| ------------- | ---------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `values`      | `in-out [float]` | `[0, 100]`     | Two-way; the stops, in ascending order. Two of them is the interval control; three or more is a multi-stop slider.                                                                                                                                                                                                         |
| `minimum`     | `in float`       | `0`            | Lower bound of the whole range (inclusive).                                                                                                                                                                                                                                                                                |
| `maximum`     | `in float`       | `100`          | Upper bound of the whole range (inclusive).                                                                                                                                                                                                                                                                                |
| `step`        | `in float`       | `1`            | The grid every user-driven value lands on, counted from `minimum` — same contract as `Slider.step`, pointer and keyboard alike. `0` turns snapping off.                                                                                                                                                                    |
| `disabled`    | `in bool`        | `false`        | When true, every thumb dims and stops responding.                                                                                                                                                                                                                                                                          |
| `labels`      | `in [string]`    | `[]`           | Accessible names for the thumbs, in `values` order. A multi-stop control is that many slider nodes, and the call site cannot reach inside to name them the way it names a `Slider`, so the names come in as a list — `["Minimum price", "Maximum price"]`. The group itself takes the call site's own `accessible-label:`. |
| `value-label` | `in string`      | `@tr("Value")` | What a thumb this list does not name is called, with its position appended: "Value 1", "Value 2". A name nobody chose is still better than an unnamed control, which is one assistive technology cannot ask for.                                                                                                           |

### Callbacks

| Callback              | Description                                                                         |
| --------------------- | ----------------------------------------------------------------------------------- |
| `changed(int, float)` | Fired with the thumb that moved and its new value. Read `values` for the whole set. |

## Accessibility

- **Multiple slider nodes.** Each thumb is an independent `slider` accessible node reporting its individual value and the dynamic bounds imposed by its neighboring thumbs.
- **Accessible names.** Thumb names are derived from the `labels` array or formatted via `value-label`.
- **Keyboard controls.** `Tab` focuses each thumb sequentially. `←`/`↓` decreases value, `→`/`↑` increases value, and `Home`/`End` jumps to the nearest boundary.
