Slider

Native <input type="range"> with two-way binding via data-model. The fill bar percentage is derived; min/max/step come from props. Keyboard, touch, and screen reader support are inherited from the platform.

Live preview

Markup

<div class="slider"
     data-component="slider-demo"
     data-label="Volume"
     data-min="0"
     data-max="100"
     data-step="5"
     data-value="50">
  <div class="slider-row">
    <span class="slider-label" data-text="label"></span>
    <input class="slider-input"
      type="range"
      data-model="value"
      data-bind="min:min, max:max, step:step,
                 style:'--fill:' + percent() + '%'" />
    <span class="slider-value" data-text="value"></span>
  </div>
</div>

Definition

Micra.define('slider-demo', {
  state: {
    label: '',
    value: 0,
    min: 0,
    max: 100,
    step: 1,
    suffix: '',
  },

  onCreate() {
    // this.prop() auto-casts numeric strings to Number — no manual cast needed.
    this.state.label  = this.prop('label', '')
    this.state.suffix = this.prop('suffix', '')
    this.state.min    = this.prop('min',  0)
    this.state.max    = this.prop('max',  100)
    this.state.step   = this.prop('step', 1)
    this.state.value  = this.prop('value', this.state.min)
  },

  // derived percentage for the WebKit fill track
  percent() {
    const { value, min, max } = this.state
    if (max === min) return 0
    return Math.round(((value - min) / (max - min)) * 100)
  },
})

Integration

  • Native input for accessibility and platform behavior — keyboard (Arrow / PageUp / Home / End), screen reader announcements, and touch drag work without custom code.
  • WebKit fill bar uses a CSS custom property: style:'--fill:' + percent() + '%' writes --fill on the input, and the ::-webkit-slider-runnable-track rule reads it. Firefox uses ::-moz-range-progress, which fills automatically.
  • Props in, value out. Initial config comes from data-* attributes. To listen to changes elsewhere, emit on input: <input @input="emit"> + emit() { Micra.emit('slider:change', this.state.value) }.
  • Numeric two-way binding. data-model on type="range" / type="number" writes the input's valueAsNumber, not a string — so state.value stays numeric without manual casting. this.prop() auto-casts numeric strings too, so min/max/step from data-* attributes arrive as numbers as well.

Pitfalls

  • Don't reinvent the slider with mousedown/move handlers — you lose keyboard, touch, ARIA, RTL, and high-contrast support all at once. The native input is the right primitive.
  • Don't store percent in state. It's a pure function of three other fields — derive on read.
  • Don't bind style: as a concatenated string when the element has other inline styles. applyBind calls setAttribute('style', …), which replaces the entire attribute and wipes any pre-existing inline rules. For mixing with other inline styles, pass an object — style:{ '--fill': percent() + '%' } — which Micra applies via Object.assign(el.style, val) and merges instead of replacing.