Slider
Control for selecting a numeric value within a range.
TailwindCSS
Vanilla JS
package showcase
import "github.com/axzilla/templui/internal/components/slider"
templ SliderDefault() {
<div class="w-full max-w-sm">
@slider.Slider() {
@slider.Input()
}
</div>
}
Installation
templui add slider
Copy and paste the following code into your project:
package slider import ( "fmt" "github.com/axzilla/templui/internal/utils" ) type Props struct { ID string Class string Attributes templ.Attributes } type InputProps struct { ID string Class string Attributes templ.Attributes Name string Min int Max int Step int Value int Disabled bool } type ValueProps struct { ID string Class string Attributes templ.Attributes For string // Corresponds to the ID of the Slider Input } templ Slider(props ...Props) { @Script() {{ var p Props }} if len(props) > 0 { {{ p = props[0] }} } <div if p.ID != "" { id={ p.ID } } class={ utils.TwMerge("w-full", p.Class) } data-slider-wrapper { p.Attributes... } > { children... } </div> } templ Input(props ...InputProps) { {{ var p InputProps }} if len(props) > 0 { {{ p = props[0] }} } if p.ID == "" { {{ p.ID = utils.RandomID() }} } <input type="range" id={ p.ID } data-slider-input if p.Name != "" { name={ p.Name } } if p.Value != 0 { value={ fmt.Sprintf("%d", p.Value) } } if p.Min != 0 { min={ fmt.Sprintf("%d", p.Min) } } if p.Max != 0 { max={ fmt.Sprintf("%d", p.Max) } } if p.Step != 0 { step={ fmt.Sprintf("%d", p.Step) } } class={ utils.TwMerge( "w-full h-2 rounded-full bg-secondary appearance-none cursor-pointer", "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", "[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4", "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-primary", "[&::-webkit-slider-thumb]:hover:bg-primary/90", "[&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:border-0", "[&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:bg-primary", "[&::-moz-range-thumb]:hover:bg-primary/90", "disabled:opacity-50 disabled:cursor-not-allowed", p.Class, ), } disabled?={ p.Disabled } { p.Attributes... } /> } templ Value(props ...ValueProps) { {{ var p ValueProps }} if len(props) > 0 { {{ p = props[0] }} } if p.For == "" { <span class="text-xs text-destructive">Error: SliderValue missing 'For' attribute.</span> } <span if p.ID != "" { id={ p.ID } } data-slider-value data-slider-value-for={ p.For } class={ utils.TwMerge("text-sm text-muted-foreground", p.Class) } { p.Attributes... } > <!-- Initial value will be set by JS --> </span> } var handle = templ.NewOnceHandle() templ Script() { @handle.Once() { <script defer nonce={ templ.GetNonce(ctx) }> (function() { // IIFE function initSlider(sliderInput) { if (sliderInput.hasAttribute('data-initialized')) return; sliderInput.setAttribute('data-initialized', 'true'); const sliderId = sliderInput.id; if (!sliderId) return; const valueElements = document.querySelectorAll(`[data-slider-value][data-slider-value-for="${sliderId}"]`); function updateValues() { valueElements.forEach(el => { el.textContent = sliderInput.value; }); } updateValues(); sliderInput.addEventListener('input', updateValues); } function initAllComponents(root = document) { if (root instanceof Element && root.matches('input[type="range"][data-slider-input]')) { initSlider(root); } for (const slider of root.querySelectorAll('input[type="range"][data-slider-input]:not([data-initialized])')) { initSlider(slider); } } const handleHtmxSwap = (event) => { const target = event.detail.elt if (target instanceof Element) { requestAnimationFrame(() => initAllComponents(target)); } }; initAllComponents(); document.addEventListener('DOMContentLoaded', () => initAllComponents()); document.body.addEventListener('htmx:afterSwap', handleHtmxSwap); document.body.addEventListener('htmx:oobAfterSwap', handleHtmxSwap); })(); // End of IIFE </script> } }
Update the import paths to match your project setup.
Examples
Value
package showcase
import "github.com/axzilla/templui/internal/components/slider"
templ SliderValue() {
<div class="w-full max-w-sm">
@slider.Slider() {
<div class="flex justify-end mb-1">
@slider.Value(slider.ValueProps{
For: "slider-value",
})
</div>
@slider.Input(slider.InputProps{
ID: "slider-value",
Value: 75,
Min: 0,
Max: 100,
Step: 1,
})
}
</div>
}
Steps
package showcase
import (
"github.com/axzilla/templui/internal/components/label"
"github.com/axzilla/templui/internal/components/slider"
)
templ SliderSteps() {
<div class="w-full max-w-sm">
@slider.Slider(slider.Props{}) {
<div class="flex justify-between items-center mb-1">
@label.Label() {
Zoom Level
}
<div class="flex items-center">
@slider.Value(slider.ValueProps{
For: "slider-steps",
})
</div>
</div>
@slider.Input(slider.InputProps{
ID: "slider-steps",
Name: "slider-steps",
Value: 100,
Min: 0,
Max: 200,
Step: 25,
})
}
</div>
}
Disabled
package showcase
import (
"github.com/axzilla/templui/internal/components/label"
"github.com/axzilla/templui/internal/components/slider"
)
templ SliderDisabled() {
<div class="w-full max-w-sm">
@slider.Slider() {
<div class="flex justify-between items-center mb-1">
@label.Label() {
Volume
}
@slider.Value(slider.ValueProps{
For: "slider-disabled",
})
</div>
@slider.Input(slider.InputProps{
ID: "slider-disabled",
Value: 20,
Min: -20,
Max: 200,
Step: 20,
Disabled: true,
})
}
</div>
}
External Value
External value (linked to the slider):
%
package showcase
import "github.com/axzilla/templui/internal/components/slider"
templ SliderExternalValue() {
<div class="space-y-6 w-full max-w-sm">
<div>
@slider.Slider() {
@slider.Input(slider.InputProps{
ID: "slider-external-value",
Value: 50,
Min: 0,
Max: 100,
Step: 1,
})
}
</div>
<div class="bg-muted p-4 rounded-md">
<p class="text-sm text-muted-foreground mb-2">External value (linked to the slider):</p>
<div class="text-3xl font-bold flex gap-1">
@slider.Value(slider.ValueProps{
For: "slider-external-value",
Class: "text-3xl font-bold text-primary",
})
%
</div>
</div>
</div>
}