# Slider A styled `` with optional label and description. Supports min/max/step/value and HTMX attributes. --- ## HTML structure ``` div.flex.flex-col.gap-1.5 label[for={id}].text-sm.font-medium ← omitted when label is empty {label} input[type=range, id, name, min, max, step, value, class, $$HxAttrs$$] p.text-sm.text-muted-foreground ← omitted when description is empty {description} ``` --- ## CSS mechanics | Class | Effect | |---|---| | `w-full h-2 rounded-lg appearance-none cursor-pointer accent-primary` | Full-width, pill-shaped track; thumb follows primary color | | `bg-secondary` | Track fill color | | `accent-primary` | Thumb and active track color follows `--color-primary` | --- ## Constructor signature ```csharp public Slider( string id, string name = "", int min = 0, int max = 100, int step = 1, int value = 50, string label = "", string description = "", string extraClasses = "", string hxAttrs = "") ``` | Parameter | Description | |---|---| | `id` | Element id and label `for` target | | `name` | Form field name | | `min` | Minimum value (default: 0) | | `max` | Maximum value (default: 100) | | `step` | Increment step (default: 1) | | `value` | Initial value (default: 50) | | `label` | Optional visible label | | `description` | Optional helper text | | `extraClasses` | Additional Tailwind classes on the input | | `hxAttrs` | Verbatim HTMX / data attributes | --- ## Usage examples ### Basic 0–100 slider ```csharp new Slider( id: "volume", name: "volume", label: "Volume") ``` ### Fixed range with step ```csharp new Slider( id: "brightness", name: "brightness", min: 10, max: 100, step: 10, value: 70, label: "Brightness", description: "10–100") ``` ### Live HTMX update ```csharp new Slider( id: "fontSize", name: "fontSize", min: 12, max: 24, value: 16, label: "Font size", hxAttrs: """hx-post="/settings/font-size" hx-trigger="change" hx-include="[name='fontSize']"""") ``` ### Reading in a form handler ```csharp public record Command([property: FromForm] int Volume); // command.Volume is the slider value at submit time ``` --- ## Tips and tricks - Display the current numeric value next to the slider by adding a small `` element and a JS `input` event listener: `slider.addEventListener('input', e => output.value = e.target.value)`. This can be added via `hxAttrs: "oninput=\"document.getElementById('vol-val').textContent=this.value\""`. - `value` is the **initial** server-rendered position. After the user moves the slider, only the form submission captures the new value. - Use `step` to snap to meaningful increments (e.g. `step: 5` for a 0–100 percentage slider). - `accent-primary` is supported in all modern browsers and requires no custom CSS. - `accent-primary` is supported in all modern browsers and requires no custom CSS. --- ## Complete page example **`Templates/AudioSettingsPage.htmx`** ```html

Audio settings

$$AntiforgeryToken$$
$$VolumeSlider$$ $$BassSlider$$ $$TrebleSlider$$
$$SaveBtn$$
$$SuccessAlert$$
``` **`Templates/AudioSettingsPage.htmx.cs`** ```csharp namespace Htmx.ApiDemo.Templates; public sealed class AudioSettingsPage : AudioSettingsPageBase { private readonly IHtmxComponent _volume; private readonly IHtmxComponent _bass; private readonly IHtmxComponent _treble; private readonly IHtmxComponent _save; private readonly IHtmxComponent _success; private readonly byte[] _afToken; public AudioSettingsPage( IAntiforgery af, HttpContext ctx, AudioPrefs? prefs = null, bool saved = false) { var tokens = af.GetAndStoreTokens(ctx); _afToken = $"""""".ToUtf8Bytes(); _volume = new Components.Slider(id: "volume", name: "volume", label: "Volume", value: prefs?.Volume ?? 70, description: "0 – 100"); _bass = new Components.Slider(id: "bass", name: "bass", label: "Bass", value: prefs?.Bass ?? 50, min: -10, max: 10, step: 1); _treble = new Components.Slider(id: "treble", name: "treble", label: "Treble", value: prefs?.Treble ?? 50, min: -10, max: 10, step: 1); _save = new Components.Button("Save", type: "submit"); _success = saved ? new Components.Alert(title: "Audio settings saved.") : HtmxEmpty.Instance; } protected override void RenderAntiforgeryToken(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_afToken); protected override void RenderVolumeSlider(HtmxRenderContext ctx) => _volume.Render(ctx.Next()); protected override void RenderBassSlider(HtmxRenderContext ctx) => _bass.Render(ctx.Next()); protected override void RenderTrebleSlider(HtmxRenderContext ctx) => _treble.Render(ctx.Next()); protected override void RenderSaveBtn(HtmxRenderContext ctx) => _save.Render(ctx.Next()); protected override void RenderSuccessAlert(HtmxRenderContext ctx) => _success.Render(ctx.Next()); } ``` **POST handler** ```csharp [Handler] [MapPost("/settings/audio")] public static partial class PostAudioSettingsHandler { public record Command( [property: FromForm] int Volume, [property: FromForm] int Bass, [property: FromForm] int Treble); private static Task HandleAsync( [AsParameters] Command cmd, HttpContext ctx, IAntiforgery af, CancellationToken ct) { var prefs = new AudioPrefs(cmd.Volume, cmd.Bass, cmd.Treble); // Persist prefs… return ctx.WriteHtmxPage( new AudioSettingsPage(af, ctx, prefs, saved: true), title: "Audio settings"); } } public record AudioPrefs(int Volume, int Bass, int Treble); ``` **`AppJsonSerializerContext.cs`** ```csharp [JsonSerializable(typeof(PostAudioSettingsHandler.Command), TypeInfoPropertyName = "AudioSettingsCommand")] ```