Components
Slider
A value chosen along a track, or a range between two thumbs. Each thumb is a real range input, so dragging, a press on the track and the keys are the browser's own.
Drawn and behaving as Flux UI's slider does, measured against its live docs.
Slider
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderExample : Component
{
private int Amount { get; set; } = 50;
protected override Component? Render() =>
Ui.Slider.Bind(() => Amount).Class("max-w-sm");
}Min/max/step
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderStepExample : Component
{
private int _amount = 50;
protected override Component? Render() =>
Ui.Slider.Value(_amount).Min(0).Max(100).Step(10).OnChange(amount => { _amount = amount; }).Class("max-w-sm");
}Displaying value
The value follows the thumb while it moves, so a label beside it can show the number.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderValueExample : Component
{
private int Amount { get; set; } = 50;
protected override Component? Render() =>
Ui.Field.Class("max-w-sm")[
Ui.Label.Trailing(Span.Class("tabular-nums").Data("testid", "ui-slider-amount")[Amount.ToString(CultureInfo.InvariantCulture)])[
"Corner radius"
],
Ui.Slider.Bind(() => Amount).Live()
];
}With input
A number input beside the slider, both bound to the same member, in one field.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderInputExample : Component
{
private int Amount { get; set; } = 25;
protected override Component? Render() =>
Ui.Field.Class("max-w-sm")[
Ui.Label["Corner radius"],
Div.Class("-mt-2 flex items-center gap-4")[
Ui.Slider.Bind(() => Amount).Live(),
Ui.Input.Bind(() => Amount).Live().Type(InputType.Number).Sm.Class("max-w-18")
]
];
}Big steps
Hold Shift and press an arrow key to move by the big step; Page Up and Page Down move by it too.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderBigStepExample : Component
{
private int _amount = 50;
protected override Component? Render() =>
Div.Class("max-w-sm space-y-3")[
Ui.Slider.Value(_amount).Step(1).BigStep(10).OnChange(amount => { _amount = amount; }),
Ui.Text[
"Amount: ",
Span.Class("tabular-nums").Data("testid", "ui-slider-big-value")[_amount.ToString(CultureInfo.InvariantCulture)]
]
];
}Amount: 50
Step marks
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderMarksExample : Component
{
private int _step = 3;
protected override Component? Render() =>
Ui.Slider.Value(_step).Min(1).Max(5).OnChange(step => { _step = step; }).Class("max-w-sm")[
Enumerable.Range(1, 5).Select(step => Ui.SliderTick.Key(step).Value(step))
];
}Step dots
Inside puts the ticks on the track, and Dot draws each as a dot.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderDotsExample : Component
{
private int _step = 4;
protected override Component? Render() =>
Ui.Slider.Value(_step).Inside.Min(1).Max(5).TrackClass("h-5").ThumbClass("size-6").OnChange(step => { _step = step; })
.Class("max-w-sm")[
Enumerable.Range(1, 5).Select(step => Ui.SliderTick.Key(step).Value(step).Dot)
];
}Numbered steps
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderNumbersExample : Component
{
private int _step = 3;
protected override Component? Render() =>
Ui.Slider.Value(_step).Min(1).Max(5).OnChange(step => { _step = step; }).Class("max-w-sm")[
Enumerable.Range(1, 5).Select(step => Ui.SliderTick.Key(step).Value(step)[step.ToString(CultureInfo.InvariantCulture)])
];
}Custom steps
A tick's children are its label. Pressing one moves the thumb there.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderCustomStepsExample : Component
{
private int _level = 3;
protected override Component? Render() =>
Ui.Slider.Value(_level).Min(1).Max(5).OnChange(level => { _level = level; }).Class("max-w-sm")[
Ui.SliderTick.Value(1)["Low"],
Ui.SliderTick.Value(3)["Mid"],
Ui.SliderTick.Value(5)["High"]
];
}Range slider
Two thumbs over an array of two: int[] Range = [20, 80].
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderRangeExample : Component
{
private int[] Range { get; set; } = [20, 80];
protected override Component? Render() =>
Ui.Slider.Bind(() => Range).Range().Class("max-w-sm");
}Min steps between
The thumbs never come closer than ten steps.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderMinStepsExample : Component
{
private int[] _range = [40, 60];
protected override Component? Render() =>
Ui.Slider.Value(_range).Range().Step(1).MinStepsBetween(10).OnChange(range => { _range = range; }).Class("max-w-sm");
}Displaying values
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderRangeValuesExample : Component
{
private int[] Range { get; set; } = [200, 800];
protected override Component? Render() =>
Div.Class("relative max-w-sm")[
Ui.Field[
Ui.Label.Trailing(Span.Data("testid", "ui-slider-price")[
"$", Span.Class("tabular-nums")[Number(Range[0])], " – $", Span.Class("tabular-nums")[Number(Range[1])]
])["Price range"],
Ui.Slider.Bind(() => Range).Live().Range().Min(0).Max(990).Step(10).MinStepsBetween(10).BigStep(100)
]
];
private static string Number(int value) => value.ToString(CultureInfo.InvariantCulture);
}Custom styles
TrackClass and ThumbClass style the two parts.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class SliderStylesExample : Component
{
private int _amount = 50;
protected override Component? Render() =>
Ui.Slider.Value(_amount).TrackClass("h-5").ThumbClass("size-5").OnChange(amount => { _amount = amount; }).Class("max-w-sm");
}Behaviour
The value follows the thumb
A slider binds a number: an int, long, float, double or decimal. The bound member (or OnChange) hears every step while the thumb is dragged, which is what lets a label beside it show the number; a slider the page owns draws the Value it was given, so keep it in state.
A range is the same slider over an array of two (int[], double[], decimal[]), and every change writes a new array. The thumbs do not cross, MinStepsBetween keeps them that many steps apart, and a press on the track moves the nearer one.
The browser does the moving
Each thumb holds a real <input type="range">, invisible and laid over the stretch of track that thumb can reach. A range's two inputs say aria-valuetext="200 start range" and "800 end range", and carry their neighbour as their own max and min. BigStep is the one thing a range input cannot do alone, so the input carries data-rask-big-step and the runtime steps it.
Key | What happens |
|---|---|
| ArrowRight, ArrowUp | One step up. With Shift, the big step. |
| ArrowLeft, ArrowDown | One step down. With Shift, the big step. |
| Page Up, Page Down | The big step, or a tenth of the track when there is none. |
| Home, End | The two ends, or the other thumb less the steps kept between them. |
A label is the field's
Flux's slider takes no label of its own, and neither does this one: put it in a Ui.Field with a Ui.Label (and a Ui.Description), and every thumb is named by that label (aria-labelledby) and described by the rest. A bound slider outside a field still shows its own message.
Ticks
Ui.SliderTick.Value(n) children mark values: a line, a .Dot, or whatever you put inside. They sit under the track, or on it with .Inside (TickPosition). Each carries data-active while the fill reaches it and data-current while a thumb is on it, and pressing one moves the thumb there. A size-6 in ThumbClass is also where the fill and the ticks learn the thumb's size (--ui-slider-thumb, 1rem unless set).
Reference
Ui.Slider
A value along a track, or a range between two thumbs. Its children are its ticks.
Step | Takes | What it does |
|---|---|---|
.Bind(…) | () => a number, or an array of two | The model's number it reads and writes: () => m.Amount, or an array of two for a range. |
.Value(…) | a number, or an array of two | The value, for a page that holds it itself. |
.OnChange(…) | a handler given the new value | Runs with every step the thumb moves. |
.AfterBind(…) | a handler given the new value | Runs after Bind has written the model. |
.Validate(…) | a validator | A rule the value is checked by when it is written. |
.Live() | bool | Writes the model as the thumb moves, not with the next action. |
.Range() | bool | Two thumbs, over an array of two. |
.Min(…) | Double | The lowest value. 0 when unset. |
.Max(…) | Double | The highest value. 100 when unset. |
.Step(…) | Double | How far one step moves the thumb. 1 when unset. |
.BigStep(…) | Double | How far Shift with an arrow, and Page Up or Page Down, move it. |
.MinStepsBetween(…) | int | How many steps a range's two thumbs keep between them. |
.TickPosition(…) | Ui.SliderTickPositionBelow Inside Each is a step of its own too, unless an HTML tag has its name. | Where the ticks sit: under the track when unset, or Inside it. |
.TrackClass(…) | string | Classes for the track: h-5. |
.ThumbClass(…) | string | Classes for the thumbs: size-5. |
.Disabled() | bool | Dims it and takes it out of reach. |
.ShowValidation() | bool | False leaves the member's message to a Ui.Error placed elsewhere. |
Ui.SliderTick
A mark at a value. Its children are its label; a line when it has none.
Step | Takes | What it does |
|---|---|---|
.Value(…) | Double | The value it marks. Required. |
.Variant(…) | Ui.SliderTickVariantLine Dot Each is a step of its own too, unless an HTML tag has its name. | A line when unset, or a Dot. |