Corner Radius Slider

An Apple-style slider component for Vue with tick marks, a minimal thumb, and smooth pointer tracking. Perfect for design tools, settings panels, or any UI that needs a polished numeric input.

Vue
sliderappleiosvuenuxttailwindinputdesign-tool

Preview

Corner Radius
23

Code

Corner Radius Slider.vue
<template>  <div class="w-full">    <span class="block text-[13px] font-medium text-[#86868b] tracking-wide uppercase mb-2.5 pl-1.5">      {{ label }}    </span>
    <div class="bg-white/80 backdrop-blur-2xl backdrop-saturate-[1.6] rounded-2xl p-1 flex items-center gap-4 shadow-[0_0_0_0.5px_rgba(0,0,0,0.06),0_2px_8px_rgba(0,0,0,0.04),0_8px_24px_rgba(0,0,0,0.06)] select-none">      <div        ref="trackRef"        class="flex-1 h-12.25 flex items-center relative cursor-ew-resize touch-none rounded-[18px]"        @pointerdown="onPointerDown"        @pointermove="onPointerMove"        @pointerup="onPointerUp"        @pointercancel="onPointerUp"      >        <!-- Ticks -->        <div class="absolute left-3 right-3 flex items-center justify-between z-0">          <div            v-for="(_, i) in tickCount"            :key="i"            class="w-[1.5px] rounded-sm transition-colors duration-150"            :class="[              i % 5 === 0 ? 'h-4' : 'h-2.5',              (i / (tickCount - 1)) * 100 <= percentage                ? 'bg-black/25'                : 'bg-black/10',            ]"          />        </div>
        <!-- Thumb -->        <div          class="bg-[#f2f2f2] rounded-lg h-full flex justify-end items-center relative z-10"          :style="{ width: percentage + '%', minWidth: '24px' }"        >          <div            class="mr-2 shrink-0 w-1 h-5.5 rounded-[2.5px] shadow-[0_0.5px_2px_rgba(0,0,0,0.12)] transition-colors duration-200"            :class="isDragging ? 'bg-black/50' : 'bg-[rgba(117,117,117,0.8)]'"          />        </div>      </div>
      <span class="text-base font-medium tabular-nums text-[#1d1d1f] min-w-8.5 text-right -tracking-[0.5px] shrink-0 mr-2">        {{ modelValue }}      </span>    </div>  </div></template>
<script setup lang="ts">import { ref, computed } from 'vue'
const props = withDefaults(defineProps<{  modelValue?: number  min?: number  max?: number  tickCount?: number  label?: string}>(), {  modelValue: 23,  min: 0,  max: 50,  tickCount: 11,  label: 'Corner Radius',})
const emit = defineEmits<{  'update:modelValue': [value: number]}>()
const trackRef = ref<HTMLElement | null>(null)const isDragging = ref(false)
const percentage = computed(  () => ((props.modelValue - props.min) / (props.max - props.min)) * 100)
function updateValue(clientX: number) {  const track = trackRef.value  if (!track) return  const rect = track.getBoundingClientRect()  const pad = 12  const ratio = Math.max(0, Math.min(1, (clientX - rect.left - pad) / (rect.width - pad * 2)))  emit('update:modelValue', Math.round(ratio * (props.max - props.min) + props.min))}
function onPointerDown(e: PointerEvent) {  isDragging.value = true  updateValue(e.clientX)  ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)}
function onPointerMove(e: PointerEvent) {  if (!isDragging.value) return  updateValue(e.clientX)}
function onPointerUp() {  isDragging.value = false}</script>
Purple gradient background

Stop recreating designs you've already seen

Join now and get 30% off your first year as an early supporter. One email on launch day. That's it.