---
title: "Composant Slider — zenit Zig UI"
description: "Choisir une valeur sur une plage continue ou par paliers. min / max / step, disabled, l'affichage de la valeur et le hit-testing track / thumb restent…"
url: https://zenit.z.express/fr/components/slider
language: fr
alternate_en: https://zenit.z.express/components/slider.md
alternate_zh: https://zenit.z.express/zh/components/slider.md
alternate_es: https://zenit.z.express/es/components/slider.md
alternate_ja: https://zenit.z.express/ja/components/slider.md
alternate_ko: https://zenit.z.express/ko/components/slider.md
alternate_de: https://zenit.z.express/de/components/slider.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# Slider

`ui.widgets.Slider`

Choisir une valeur sur une plage continue ou par paliers.

[Video](https://zenit.z.express/media/stories/slider.mp4?v=1077699602)

**BEHAVIOR CONTRACT**

min / max / step, disabled, l'affichage de la valeur et le hit-testing track / thumb restent synchronisés.

**RECORDED INTERACTION**

Le harness clique sur la piste à 82 % ; le thumb et la valeur se mettent à jour ensemble.

`examples/storybook/stories.zig:278`

```zig
pub fn buildSlider(scope: *ui.Scope, cx: *ui.Cx) anyerror!*ui.Node {
    const a = cx.allocator;
    const c = try col(cx, 16);

    const s1 = try ui.widgets.Slider(.{
        .min = 0,
        .max = 100,
        .initial_value = 40,
        .width = 320,
        .show_value = true,
        .test_id = "story.slider.track",
    }).mount(scope, cx);
    s1.wrapper.meta.ownership.meta.test_id = "story.slider.basic";

    const s2 = try ui.widgets.Slider(.{
        .min = 0,
        .max = 10,
        .step = 1,
        .initial_value = 5,
        .width = 320,
        .show_value = true,
        .test_id = "story.slider.stepped.track",
    }).mount(scope, cx);
    s2.wrapper.meta.ownership.meta.test_id = "story.slider.stepped";
    const continuous = try ui.widgets.Slider(.{
        .min = 0,
        .max = 10,
        .step = 0,
        .initial_value = 5,
        .width = 320,
        .show_value = true,
        .test_id = "story.slider.continuous.track",
    }).mount(scope, cx);
    const status = try label(cx, "");
    status.meta.ownership.meta.test_id = "story.slider.feedback.status";
    const pattern_status = try label(cx, "");
    pattern_status.meta.ownership.meta.test_id = "story.slider.feedback.pattern";
    const state = try scope.allocator.create(SliderFeedbackStory);
    state.* = .{ .basic = s1, .stepped = s2, .continuous = continuous, .status = status, .pattern_status = pattern_status };
    try scope.adoptResource(state, SliderFeedbackStory.destroy);
    s2.state.on_change = ui.Cx.handlerFrom(SliderFeedbackStory, state, SliderFeedbackStory.refresh);
    continuous.state.on_change = ui.Cx.handlerFrom(SliderFeedbackStory, state, SliderFeedbackStory.refresh);
    state.refresh();

    try c.appendChild(a, try label(cx, "Haptic feedback — opt in to try"));
    const toggle = try ui.widgets.Switch(.{
        .label_text = "Enable trackpad feedback",
        .initial_checked = false,
        .on_change = ui.Cx.boolHandlerFrom(SliderFeedbackStory, state, SliderFeedbackStory.toggle),
    }).mount(scope, cx);
    toggle.meta.ownership.meta.test_id = "story.slider.feedback.toggle";
    try c.appendChild(a, toggle);
    const pattern_options = [_]ui.widgets.checkbox.RadioOption{
        .{ .value = "alignment", .label_text = "Alignment" },
        .{ .value = "generic", .label_text = "Generic" },
        .{ .value = "level_change", .label_text = "Level change" },
    };
    const patterns = try ui.widgets.RadioGroup(.{
        .options = &pattern_options,
        .value = "alignment",
        .horizontal = true,
        .on_change = ui.Cx.strHandlerFrom(SliderFeedbackStory, state, SliderFeedbackStory.selectPattern),
    }).mount(scope, cx);
    for (patterns.children.items, [_][]const u8{
        "story.slider.pattern.alignment", "story.slider.pattern.generic", "story.slider.pattern.level_change",
    }) |option, test_id| option.meta.ownership.meta.test_id = test_id;
    try c.appendChild(a, patterns);
    try c.appendChild(a, pattern_status);
    try c.appendChild(a, try label(cx, "Native patterns, not intensity levels. Alignment is the default for step snapping."));
    try c.appendChild(a, status);
    try c.appendChild(a, try label(cx, "macOS Force Touch trackpad; device and system settings determine the feel."));
    try c.appendChild(a, try label(cx, "0–100, value 40"));
    try c.appendChild(a, s1.wrapper);
    try c.appendChild(a, try label(cx, "0–10 step 1, value 5"));
    try c.appendChild(a, s2.wrapper);
    try c.appendChild(a, try label(cx, "Continuous (step 0) — no feedback"));
    try c.appendChild(a, continuous.wrapper);
    const reset = try ui.widgets.Button(.{
        .label = "Reset both to 5 (no feedback)",
        .variant = .secondary,
        .on_click = ui.Cx.handlerFrom(SliderFeedbackStory, state, SliderFeedbackStory.reset),
    }).mount(scope, cx);
    reset.meta.ownership.meta.test_id = "story.slider.feedback.reset";
    try c.appendChild(a, reset);

    const s3 = try ui.widgets.Slider(.{ .initial_value = 30, .width = 320, .disabled = true }).mount(scope, cx);
    try c.appendChild(a, try label(cx, "Disabled"));
    try c.appendChild(a, s3.wrapper);

    return c;
}
```

[Voir le code sur GitHubexamples/storybook/stories.zig:278](https://github.com/version-next/zenit/blob/HEAD/examples/storybook/stories.zig#L278)

Le code source tel quel de la story dans le Storybook (zenit 5f9add5+wip 2026-09-30). `W` désigne `ui.widgets` ; `col` / `row` / `label` sont de petits helpers de mise en page du Storybook.

### SliderProps

`ui.widgets.Slider`

[src/ui/components/slider/mod.zig:33](https://github.com/version-next/zenit/blob/HEAD/src/ui/components/slider/mod.zig#L33)

```zig
fn Slider(props: SliderProps) SliderBuilder
```

| Champ | Type | Défaut | Description |
| --- | --- | --- | --- |
| `min` | `f32` | `0` | — |
| `max` | `f32` | `100` | — |
| `step` | `f32` | `1` | Positive values snap to discrete steps. Zero disables snapping. |
| `haptic_feedback` | `bool` | `false` | Opt in to native feedback when pointer input changes a snapped value. Applications may bind this to their user's preference. |
| `haptic_pattern` | `HapticFeedbackPattern` | `.alignment` | Native semantic pattern, not a weak/medium/strong intensity scale. |
| `initial_value` | `f32` | `0` | 初始值。\*\*只在 mount 时读取一次\*\* —— 之后组件持有自己的 SliderState。需要外部驱动请用 mount 返回的 \`result.state.setValue()\` （那条路径会同步视觉与 a11y 数值）。 |
| `disabled` | `bool` | `false` | — |
| `width` | `f32` | `200` | — |
| `track_height` | `f32` | `4` | — |
| `thumb_size` | `f32` | `16` | — |
| `show_value` | `bool` | `false` | — |
| `on_change` | `?core.HandlerRef` | `null` | — |
| `test_id` | `?[]const u8` | `null` | 给 track 节点设置的 test\_id（供 E2E 测试定位） |

### SliderResult

`ui.widgets.Slider`

[src/ui/components/slider/mod.zig:173](https://github.com/version-next/zenit/blob/HEAD/src/ui/components/slider/mod.zig#L173)

| Champ | Type | Défaut | Description |
| --- | --- | --- | --- |
| `wrapper` | `*Node` | — | — |
| `state` | `*SliderState` | — | — |
