Packages

A shadcn/ui-inspired UI library for Gleam and Lustre

Current section

Files

Jump to
glizzy src ui slider.gleam
Raw

src/ui/slider.gleam

// MIT License
// Copyright (c) 2026 Koncreate
// See LICENSE for details
import gleam/int
import gleam/option.{type Option, None, Some}
import gleam/string
import lustre/attribute.{type Attribute, attribute, class, role, type_}
import lustre/element.{type Element}
import lustre/element/html.{input}
import ui/css
import ui/keyboard
import ui/size as size_helpers
pub type Variant {
Default
Muted
}
pub type Size {
Small
Medium
Large
}
pub fn slider(attributes: List(Attribute(a))) -> Element(a) {
input([
role("slider"),
class(
[
"w-full cursor-pointer appearance-none bg-transparent",
// Thumb styling - WebKit
"[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:relative [&::-webkit-slider-thumb]:-mt-0.5",
"[&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-primary",
"[&::-webkit-slider-thumb]:transition-colors [&::-webkit-slider-thumb]:focus:outline-none [&::-webkit-slider-thumb]:focus:ring-2 [&::-webkit-slider-thumb]:focus:ring-ring [&::-webkit-slider-thumb]:focus:ring-offset-2",
// Thumb styling - Mozilla
"[&::-moz-range-thumb]:appearance-none [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-primary [&::-moz-range-thumb]:border-0",
"[&::-moz-range-thumb]:transition-colors [&::-moz-range-thumb]:focus:outline-none [&::-moz-range-thumb]:focus:ring-2 [&::-moz-range-thumb]:focus:ring-ring [&::-moz-range-thumb]:focus:ring-offset-2",
// Track styling - WebKit
"[&::-webkit-slider-runnable-track]:h-2 [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-input",
"[&::-webkit-slider-runnable-track]:transition-colors",
// Track styling - Mozilla
"[&::-moz-range-track]:h-2 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-input",
"[&::-moz-range-track]:transition-colors",
// Focus states
css.focus_ring(),
// Disabled states
css.disabled(),
// Vertical alignment
"align-middle",
]
|> string.join(" "),
),
type_("range"),
..attributes
])
}
pub fn variant(v: Variant) -> Attribute(a) {
case v {
Default -> class("bg-primary")
Muted -> class("bg-muted")
}
}
pub fn size(s: Size) -> Attribute(a) {
case s {
Small -> class(size_helpers.track_class(size_helpers.Small))
Medium -> class(size_helpers.track_class(size_helpers.Medium))
Large -> class(size_helpers.track_class(size_helpers.Large))
}
}
pub fn thumb_size(s: Size) -> Attribute(a) {
case s {
Small ->
class(
"[&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:w-3 [&::-moz-range-thumb]:h-3 [&::-moz-range-thumb]:w-3",
)
Medium ->
class(
"[&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:w-4",
)
Large ->
class(
"[&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-moz-range-thumb]:h-5 [&::-moz-range-thumb]:w-5",
)
}
}
pub fn min(value: Int) -> Attribute(a) {
attribute("min", int.to_string(value))
}
pub fn max(value: Int) -> Attribute(a) {
attribute("max", int.to_string(value))
}
pub fn value(value: Int) -> Attribute(a) {
attribute("value", int.to_string(value))
}
pub fn aria_label(label: String) -> Attribute(a) {
attribute("aria-label", label)
}
pub fn aria_valuenow(value: Int) -> Attribute(a) {
attribute("aria-valuenow", int.to_string(value))
}
pub fn aria_valuemin(value: Int) -> Attribute(a) {
attribute("aria-valuemin", int.to_string(value))
}
pub fn aria_valuemax(value: Int) -> Attribute(a) {
attribute("aria-valuemax", int.to_string(value))
}
// ============================================================================
// Keyboard Navigation
// ============================================================================
/// Keyboard messages for slider component.
pub type Msg {
/// Decrease value (ArrowLeft or ArrowDown)
Decrease
/// Increase value (ArrowRight or ArrowUp)
Increase
/// Set to minimum value (Home)
SetMin
/// Set to maximum value (End)
SetMax
/// Increase value by larger step (PageUp)
PageIncrease
/// Decrease value by larger step (PageDown)
PageDecrease
}
/// Keymap for slider keyboard navigation.
/// Follows WAI-ARIA slider pattern:
/// - ArrowLeft/ArrowDown: Decrease value
/// - ArrowRight/ArrowUp: Increase value
/// - Home: Set to minimum value
/// - End: Set to maximum value
/// - PageUp: Increase by larger step (optional)
/// - PageDown: Decrease by larger step (optional)
pub fn keymap(
key_event: keyboard.KeyEvent,
_orientation: Orientation,
) -> Option(Msg) {
case keyboard.decode_key(key_event.key) {
keyboard.Home -> Some(SetMin)
keyboard.End -> Some(SetMax)
keyboard.PageUp -> Some(PageIncrease)
keyboard.PageDown -> Some(PageDecrease)
keyboard.ArrowLeft | keyboard.ArrowDown -> Some(Decrease)
keyboard.ArrowRight | keyboard.ArrowUp -> Some(Increase)
_ -> None
}
}
/// Slider orientation.
pub type Orientation {
Horizontal
Vertical
}
/// Set slider orientation.
pub fn orientation(o: Orientation) -> Attribute(a) {
case o {
Horizontal -> attribute("aria-orientation", "horizontal")
Vertical -> attribute("aria-orientation", "vertical")
}
}