Unit Input
CSS-unit input with built-in deg/%/px/rem/em/vw/vh validators, pointer-locked drag scrubbing, and tiered typing tiers from casual to strict.
Basic Usage
Two UnitInputs, two units. Type a number; commit on blur or Enter. Arrow keys step ±1, Shift+Arrow ±10. Hover the suffix to scrub.
45deg, 50%Pointer-lock Scrub
Hover the px suffix → cursor turns into ↔ → drag horizontally. The cursor disappears (pointer-lock) and the value tracks your delta. Shift = coarse (×10), Alt = fine (÷10).
16pxStrict Typing
unit="deg" narrows the value/onChange types to DegString. The deg() call-site helper rejects wrong-suffix literals at compile time.
90degAPI
Public surface — component props, runtime helpers, and the type exports.
§ Component
<UnitInput<TUnit extends KnownUnit | (string & {})>
value: UnitStringMap[TUnit] | (string & {}) | string
onChange: (next: UnitStringMap[TUnit] | string) => void
unit: TUnit
min?: number
max?: number
step?: number
precision?: number
dragSensitivity?: number
prefix?: React.ReactNode
suffix?: React.ReactNode
disabled?: boolean
aria-label?: string
className?: string
/>valueUnitStringMap[TUnit] | (string & {})Current value, e.g. "45deg". Any string accepted; IntelliSense narrows by `unit`.onChange(next) => voidCalled on commit (blur, Enter, arrow, scrub). Return type narrows by `unit`.unitKnownUnit | (string & {})Suffix label. Known units (deg/%/px/rem/em/vw/vh) get strict typing; unknown widens to string.minnumber?Inclusive lower bound. Soft-clamps on commit.maxnumber?Inclusive upper bound. Soft-clamps on commit.stepnumber (default 1)Arrow-key step. Shift=×10, Alt=÷10.precisionnumber (default 0)Decimal places. Round via toFixed on commit.dragSensitivitynumber (default 1)Scrub: 1px = step × sensitivity.prefixReact.ReactNode?Left-side slot inside the shell, shares focus ring.suffixReact.ReactNode?Override the default unit-text suffix. Scrub handle attaches here.disabledbooleanDisables typing, arrow nudge, and scrub.aria-labelstringRequired when no visible label is associated externally.classNamestringApplied to the shell wrapper for sizing/spacing.§ Runtime helpers
deg<S extends string>(value: S & DegLiteral<S>): S
Validate a deg literal at the call site.
percent<S extends string>(value: S & PercentLiteral<S>): S
Validate a % literal at the call site.
px<S extends string>(value: S & PxLiteral<S>): S
Validate a px literal.
rem<S extends string>(value: S & RemLiteral<S>): S
Validate a rem literal.
em<S extends string>(value: S & EmLiteral<S>): S
Validate an em literal.
vw<S extends string>(value: S & VwLiteral<S>): S
Validate a vw literal.
vh<S extends string>(value: S & VhLiteral<S>): S
Validate a vh literal.
§ Types
UnitStringUnion of all 7 suggestion strings (DegString | PercentString | …).UnitStringMap{ deg: DegString, "%": PercentString, px: PxString, rem: RemString, em: EmString, vw: VwString, vh: VhString }KnownUnitkeyof UnitStringMap — "deg" | "%" | "px" | "rem" | "em" | "vw" | "vh".UnitLiteral<S>Union of all strict literal validators. Returns S if S is a valid unit literal in any of the known shapes, otherwise never.Drop it in
One command via the shadcn CLI.
$ pnpm dlx shadcn@latest add https://turtiesocks.github.io/ridiculous/r/unit-input.json