---
title: Knob
description: Knobs let users make compact, rotary adjustments to a numeric value.
category: Forms
---
import { meta as KnobMeta, Default, ValuePrecision } from '@/content/stories/knob';
import Canvas from '@/components/component-docs/canvas';
import Demo from '@/components/component-docs/demo';
export const config = {
dependencies: {
components: [Canvas, Demo],
scripts: [
'../../components/component-docs/demo.script.tsx',
'../../components/component-docs/canvas.script.tsx',
'../../components/component-docs/controls.script.tsx',
],
},
};
# Knob
Knobs let users make compact, rotary adjustments to a numeric value. They are particularly useful for controls such as gain, pan, and effect parameters.
## Try it
## Usage
Set `min`, `max`, and `step` to define the range. The knob’s visible arc spans 300°, leaving a gap that clearly separates the range endpoints.
```tsx
import { RuiKnob } from '@ecopages/radiant-ui/knob';
import { RuiField } from '@ecopages/radiant-ui/field';
import { RuiLabel } from '@ecopages/radiant-ui/label';
Gain
```
## Custom markup
`` coordinates any light-DOM tree that matches its query contract. The `RuiKnob` helper stamps these targets; it is not required.
```tsx
import '@ecopages/radiant-ui/knob';
Gain
```
BEM classes are presentation-only. The host owns control `aria-*`, ring geometry, and readout text. The `RuiKnob` view seeds those so SSR matches the hydrated control. Set `name` on the host to submit with a native form.
Use `valuePosition="below"` to place the readout under the knob, or `showValue={false}` when the value is shown elsewhere. `valueTemplate` replaces the first `{value}` token with the formatted value.
`step` is the snap interval for the stored value. `valuePrecision` only formats the readout, `aria-valuetext`, and related display text. It defaults to the decimal places in `step`, so `0.1 + 0.2` can still be stored as a binary float while the UI shows `0.3`. Round or transform committed values in application code when you need a canonical number.
```tsx
```
Set `--rui-knob-size` for CSS-controlled sizing. The numeric `size` prop overrides this variable when a specific pixel diameter is needed.
```css
rui-knob[data-size='large'] {
--rui-knob-size: 5rem;
}
```
## Theming
The knob consumes semantic roles by default. Override its public custom properties to adapt the ring without replacing component CSS. Unfilled track contrast is shared with sliders (`--rui-track-mix`, `--rui-track-fill`, `--rui-track-color`).
| Part | Default role | Custom property |
| --- | --- | --- |
| Track | `--rui-track-fill` | `--rui-knob-track-color` |
| Progress | `primary` | `--rui-knob-value-color` |
| Readout | `on-surface` | `--rui-knob-text-color` |
| Diameter | `3rem` | `--rui-knob-size` |
| Focus ring | `focus-ring` | — |
Set the mix or a solid color on an ancestor to theme slider and knob together:
```css
:root {
--rui-track-mix: 28%;
}
.panel {
--rui-track-color: var(--surface-container-high);
}
```
## API
`RuiKnob` is a custom element (``) with a JSX helper of the same name.
### Attributes
| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `number` | `50` | Selected value. |
| `min` | `number` | `0` | Range minimum. |
| `max` | `number` | `100` | Range maximum. |
| `step` | `number` | `1` | Pointer and keyboard snap interval. |
| `value-precision` | `number` | decimal places in `step` | Maximum fraction digits in the value readout. |
| `disabled` | `boolean` | `false` | Disables interaction. |
| `read-only` | `boolean` | `false` | Blocks changes but allows focus. |
| `label` | `string` | `''` | Visible and accessible name. |
| `name` | `string` | `''` | Form field name on this host. |
| `size` | `number` | — | Explicit visible SVG diameter in pixels; overrides `--rui-knob-size`. |
| `stroke-width` | `number` | `14` | Progress ring width in view-box units. |
| `show-value` | `boolean` | `true` | Shows the value inside the ring. |
| `value-position` | `center` · `below` | `center` | Places the value inside or below the knob. |
| `value-template` | `string` | `'{value}'` | Readout template. |
### Light-DOM contract
| Target | Required | Host writes | Author owns |
| --- | --- | --- | --- |
| `[data-ref="root"]` | yes | `rui-knob--value-below`, `--rui-knob-size` on host | root wrapper |
| `[data-ref="control"]` | yes | `aria-valuemin`, `aria-valuemax`, `aria-valuenow`, `aria-valuetext`, `aria-label`, `aria-readonly`, `disabled` | control button; also stamp `data-knob-control` for fields. View seeds `aria-valuenow` |
| `[data-ref="track"]` | yes | `r`, `stroke-width`, `stroke-dasharray` | SVG track circle; view seeds geometry |
| `[data-ref="progress"]` | yes | `r`, `stroke-width`, `stroke-dasharray` | SVG progress circle; view seeds geometry |
| `[data-ref="label"]` | no | `hidden`, `textContent` | visible label |
| `[data-ref="centerValue"]` | no | `textContent`, `hidden` | in-ring readout; view seeds the formatted value |
| `[data-ref="belowValue"]` | no | `textContent`, `hidden` | below-ring readout; view seeds the formatted value |
Do not set control `tabindex`. Nested hosts: none. The `RuiKnob` view seeds readout text, ring geometry, and `aria-valuenow` so the first paint matches the hydrated control.
### Events
| Event | Detail | Description |
| --- | --- | --- |
| `rui-change` | `{ value: number }` | Emitted as pointer or keyboard interaction changes the value. |
### View helpers
| Component | Target stamped | Notes |
| --- | --- | --- |
| `RuiKnob` | full `[data-ref]` tree under `[data-ref="root"]` | Stamps control, ring SVG, and readouts. Seeds readout text and ring geometry for SSR. |
### CSS classes
| Class | Description |
| --- | --- |
| `.rui-knob` | Root; wraps the label and control. |
| `.rui-knob--value-below` | Root when the value is below the control. |
| `.rui-knob__label` | Optional visible label. |
| `.rui-knob__control` | Focusable slider and pointer target. |
| `.rui-knob__svg` | SVG ring. |
| `.rui-knob__track` | Unfilled range arc. |
| `.rui-knob__progress` | Filled range arc. |
| `.rui-knob__value` | Value readout inside the ring. |
## Accessibility
- The interactive surface exposes `role="slider"` with its current, minimum, and maximum values.
- Arrow keys adjust by one step; Page Up/Down adjust by ten steps; Home and End move to the range endpoints.
- The pointer target is at least 44 × 44 pixels, including when a smaller visible SVG size is requested.