Inputs

Vertical Slider

A vertical slider for values where "higher means more": the rotation comes from writing-mode, so layout and keyboard both stay sane.

  • slider
  • vertical
  • range
  • writing-mode

Preview

1440pxHost theme

Use it with AI

  1. 1. Copy the link.
  2. 2. Write to your agent in your own words and drop the link into the sentence.
  3. 3. The agent opens the link and installs the component from the registry.

put this in the header: https://vibeui.ru/c/slider-007?lang=en

65%

Show the full instructions

What the link says, spelled out. Use it when your agent cannot open links — paste this text instead.

# Install and place "slider-007" (Vertical Slider) from VibeUI

## 1. Install first — do not skip, do not recreate
Run this exact command before writing any code:
npx shadcn@latest add https://vibeui.ru/r/slider-007.json

Registry item: https://vibeui.ru/r/slider-007.json
Installs to: components/vibeui/slider-007.tsx (the exact path follows this project's components.json aliases).
npm dependencies: none.
Registry dependencies: none.

Install it from the registry. Do not recreate it from the description,
do not substitute a similar component from another library, and do not
rewrite it to match the project's existing style.

## 2. What it is
A vertical slider for values where "higher means more": the rotation comes from writing-mode, so layout and keyboard both stay sane.

A vertical slider built on writing-mode with a scale beside it. One file, zero dependencies, a client component.

## 3. How to use it
import { Slider007 } from "@/components/vibeui/slider-007"

<Slider007
  label="Lamp brightness"
  defaultValue={40}
/>

Read the installed file for the full prop list.

## 4. Where to place it
This is a small inline component. Put it exactly where the user asked,
inside the existing markup. Do not create a new page, section or
wrapper for it. If a similar control already sits in that spot,
replace it instead of adding a second one.

Placement: ___
(The user fills this line in. If it is still blank, ask where to put it
instead of guessing.)

## 5. Keep exactly as installed
- the local --vibeui-slider-007-* palette — do not swap it for your theme tokens
- writing-mode instead of rotate: an input rotated by transform keeps a horizontal box and covers its neighbours
- direction:rtl — without it the up arrow decreases the value, contradicting the visible scale
- the to-top gradient: the fill has to grow upwards along with the thumb
- aria-valuetext with the unit — a percentage without its sign reads as a dimensionless number
- the component's own light surface — without it the dark text disappears on a dark page

## 6. You may change
- the caption through label and the bounds through min and max
- the starting value through defaultValue
- the unit of measure through unit
- the track height in the block CSS

## 7. Rules
- A vertical range on writing-mode is supported by modern browsers; on old builds it lays out horizontally but keeps working.
- The vertical form travels badly to phones: an up-down gesture competes with page scrolling.
- The scale carries only three captions: more will not read at this height.
- Do not lift the CSS variables into globals.css: the component has to stay a single file.

## 8. Verify
- it renders with no console errors;
- it looks like the preview on the VibeUI page you copied this from;
- if it does not, you changed something listed in section 5 — put it back.

For developers

npx shadcn@latest add https://vibeui.ru/r/slider-007.json
https://vibeui.ru/r/slider-007.json

Клиентский компонент: useState держит значение, из него считается доля заливки. Вертикальность задана writing-mode:vertical-lr вместе с direction:rtl, а не трансформом: повёрнутый rotate элемент остаётся горизонтальным для раскладки и накрывает соседей своей коробкой. Заливка идёт градиентом to top. Шкала стоит столбиком рядом и подписана сверху вниз. Значение объявляется через aria-valuetext. Палитра в --vibeui-slider-007-*.

Component source

The same file your agent installs. Here in case you would rather copy it by hand.

"use client"

import { useId, useState } from "react"
import type { ComponentPropsWithoutRef, CSSProperties } from "react"

export type Slider007Props = Omit<
  ComponentPropsWithoutRef<"div">,
  "children" | "defaultValue" | "onChange"
> & {
  label?: string
  min?: number
  max?: number
  step?: number
  defaultValue?: number
  unit?: string
  accent?: string
}

// Идея компонента: вертикальный ползунок для величин, которые в голове
// «выше — больше»: яркость, уровень, температура. Поворот сделан не
// трансформом, а writing-mode: элемент честно занимает вертикальную коробку,
// поэтому раскладка вокруг него не разъезжается и клавиатура работает как
// ожидается — стрелка вверх увеличивает значение.
const STYLES = `
:where([data-vibeui-block="slider-007"]){
--vibeui-slider-007-bg:oklch(1 0 0);
--vibeui-slider-007-fg:oklch(0.22 0.014 265);
--vibeui-slider-007-muted:oklch(0.55 0.014 265);
--vibeui-slider-007-border:oklch(0.9 0.006 265);
--vibeui-slider-007-track:oklch(0.92 0.006 265);
--vibeui-slider-007-accent:oklch(0.62 0.15 85);
--vibeui-slider-007-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
--vibeui-slider-007-fill:50%;
}
[data-vibeui-block="slider-007"]{
display:flex;flex-direction:column;align-items:center;gap:0.625rem;
width:100%;max-width:9rem;box-sizing:border-box;padding:0.875rem;
background:var(--vibeui-slider-007-bg);
border:1px solid var(--vibeui-slider-007-border);border-radius:0.875rem;
font-family:var(--vibeui-slider-007-font);color:var(--vibeui-slider-007-fg);
text-align:center;
}
[data-vibeui-block="slider-007"] [data-part="label"]{font-size:0.75rem;font-weight:600;line-height:1.3}
[data-vibeui-block="slider-007"] [data-part="rail"]{display:flex;align-items:center;gap:0.625rem}
/* writing-mode вместо rotate: повёрнутый трансформом input остаётся
   горизонтальным для раскладки и накрывает соседей своей коробкой. */
[data-vibeui-block="slider-007"] input{
appearance:none;-webkit-appearance:none;
writing-mode:vertical-lr;direction:rtl;
width:1.25rem;height:9rem;margin:0;padding:0;background:none;cursor:pointer;
}
[data-vibeui-block="slider-007"] input::-webkit-slider-runnable-track{
width:0.5rem;border-radius:9999px;
background:linear-gradient(to top,var(--vibeui-slider-007-accent) var(--vibeui-slider-007-fill),var(--vibeui-slider-007-track) var(--vibeui-slider-007-fill));
}
[data-vibeui-block="slider-007"] input::-moz-range-track{
width:0.5rem;border-radius:9999px;
background:linear-gradient(to top,var(--vibeui-slider-007-accent) var(--vibeui-slider-007-fill),var(--vibeui-slider-007-track) var(--vibeui-slider-007-fill));
}
[data-vibeui-block="slider-007"] input::-webkit-slider-thumb{
appearance:none;-webkit-appearance:none;margin-left:-0.3125rem;
width:1.125rem;height:1.125rem;border-radius:9999px;
background:var(--vibeui-slider-007-bg);border:3px solid var(--vibeui-slider-007-accent);
box-shadow:0 1px 4px oklch(0.2 0.02 265 / 30%);
}
[data-vibeui-block="slider-007"] input::-moz-range-thumb{
width:1.125rem;height:1.125rem;border-radius:9999px;box-sizing:border-box;
background:var(--vibeui-slider-007-bg);border:3px solid var(--vibeui-slider-007-accent);
}
[data-vibeui-block="slider-007"] input:focus-visible{outline:2px solid var(--vibeui-slider-007-accent);outline-offset:3px;border-radius:0.75rem}
/* Шкала стоит рядом столбиком и подписана сверху вниз: снизу минимум,
   сверху максимум — так же, как читается сам ползунок. */
[data-vibeui-block="slider-007"] [data-part="scale"]{
display:flex;flex-direction:column;justify-content:space-between;
height:9rem;margin:0;padding:0;list-style:none;
font-size:0.625rem;color:var(--vibeui-slider-007-muted);font-variant-numeric:tabular-nums;
}
[data-vibeui-block="slider-007"] [data-part="value"]{
font-size:1rem;font-weight:700;font-variant-numeric:tabular-nums;
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="slider-007"] *{animation:none!important;transition:none!important}}
`

/**
 * Вертикальный ползунок на writing-mode: клавиатура и раскладка не ломаются.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Slider007({
  label = "Яркость лампы",
  min = 0,
  max = 100,
  step = 5,
  defaultValue = 65,
  unit = "%",
  accent,
  className,
  style,
  ...props
}: Slider007Props) {
  const id = useId()
  const [value, setValue] = useState(defaultValue)

  const palette = {
    "--vibeui-slider-007-fill": `${((value - min) / (max - min)) * 100}%`,
    ...(accent ? { "--vibeui-slider-007-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-slider-007" precedence="medium">
        {STYLES}
      </style>
      <div
        {...props}
        data-vibeui-block="slider-007"
        className={className}
        style={palette}
      >
        <label data-part="label" htmlFor={id}>
          {label}
        </label>
        <div data-part="rail">
          <input
            id={id}
            type="range"
            min={min}
            max={max}
            step={step}
            value={value}
            aria-valuetext={`${value}${unit}`}
            onChange={(event) => setValue(Number(event.target.value))}
          />
          <ul data-part="scale" aria-hidden="true">
            <li>{max}</li>
            <li>{Math.round((min + max) / 2)}</li>
            <li>{min}</li>
          </ul>
        </div>
        <p data-part="value" role="status">
          {value}
          {unit}
        </p>
      </div>
    </>
  )
}