Inspector Unit

A number and the unit that gives it meaning, in one row: px, rem, a percentage, or a keyword like auto that is the whole value on its own. Each unit may bring its own range, so a percentage stops at 100 while pixels do not.

Installation

Adds the component and everything it depends on to your project.

npx shadcn add https://ui.beste.co/component/r-base/inspector-unit

New here? Read the installation guide.

Usage

The import and the props worth knowing about, in one place.

import { InspectorUnit } from "@/components/beste/component/inspector-unit";

// Uncontrolled
<InspectorUnit label="Width" defaultValue={{ value: 100, unit: "%" }} />

// Controlled, with a separate commit for expensive work
<InspectorUnit
  label="Max width"
  value={width}
  onValueChange={setWidth}
  onValueCommit={(value) => save(value)}
/>

// A unit that is the whole value: the number field steps aside rather than sitting
// there holding a figure that means nothing
<InspectorUnit
  label="Height"
  units={["px", "%", "vh", { value: "auto", valueless: true }]}
  value={height}
  onValueChange={setHeight}
/>

// A range per unit, since 0 to 100 reads as a percentage and 0 to 9999 does not
<InspectorUnit
  label="Offset"
  units={[
    { value: "px", min: -400, max: 400 },
    { value: "%", min: -100, max: 100 },
    { value: "rem", min: -24, max: 24, step: 0.25 },
  ]}
  value={offset}
  onValueChange={setOffset}
/>

<InspectorUnit
  label="Letter spacing"
  icon={TypeIcon}     // optional leading icon
  tone="outline"      // "muted" (default) | "outline" | "ghost"
  size="sm"           // "sm" | "default" | "lg"
  units={["em", "px"]}
  step={0.01}
  precision={3}       // decimals kept on commit
  name="tracking"     // submits as tracking and tracking-unit
  value={tracking}
  onValueChange={setTracking}
/>

// What the value turns into
const css = value.unit === "auto" ? "auto" : `${value.value}${value.unit}`;

// The arrows work the field the way they work a stepper, and Shift moves ten steps.

Playground

Turn the props on the right; the snippet under them is what you would write to get what you see.

Usage
import { InspectorUnit } from "@/components/beste/component/inspector-unit";

<InspectorUnit
  label="Width"
  className="w-72"
  defaultValue={{"value":100,"unit":"%"}}
  units={["px","%","rem",{"value":"auto","label":"auto","valueless":true}]}
/>

Keyboard and gestures

Every one of these is in the component already. They are listed because a props table cannot mention a gesture, so nothing else on this page can tell you they exist.

Arrow Up / DownOne step on the number, the way a stepper moves: a length is a number first and a string second.
Shift + ArrowTen steps.
EnterAccept what was typed.
EscapeThrow the draft away and put the committed value back.

Props

Read from the component's own type, so this cannot drift from what it accepts.

PropTypeDefaultDescription
label*stringLabel rendered on the left, inside the row.
iconLucideIconOptional leading icon shown before the label.
valueUnitValueControlled value. Pair it with `onValueChange`.
defaultValueUnitValueInitial value in uncontrolled mode.
onValueChange(value: UnitValue) => voidFires on every accepted edit and on every unit change.
onValueCommit(value: UnitValue) => voidFires once an edit is finished — the field left or Enter pressed, a unit chosen. Use it for work too expensive to run per keystroke.
units(string | InspectorUnitOption)[]["px", "%", "rem"]The units on offer. Plain strings are enough when the unit is its own label.
minnumber0 to 9999, in steps of 1Range the number answers to, for units that do not name their own.
maxnumber
stepnumber
precisionnumber2How many decimals a value keeps. Anything typed past this is rounded on commit, so what the field shows is what the caller was handed.
disabledbooleanBlock interaction and dim the row.
tone"muted" | "outline" | "ghost""muted"Surface treatment: filled (default), hairline outline, or bare until hover.
size"sm" | "default" | "lg""default"Row height preset.
namestringName of the number field, so the row can take part in a form.
idstringId of the number field, for an external `<label htmlFor>`.
classNamestring
aria-labelstringAccessible name. Falls back to `label`.

More Inspector components

View all Inspector
Contrast5.30:1

Inspector Meter

The row that reports rather than asks: a measurement the work produced, shown in the family's surface so it can sit among the settings that caused it. Threshold bands colour the bar and the number without the caller recomputing them, and it is a meter rather than a progress bar, which is how it is announced.

Applies to every breakpoint.

Inspector Note

The line of prose that belongs between rows: what a setting is for, why one is switched off, what a value will cost. Four tones, each with its own mark, and the same text size as the rows around it.

Inspector Aspect

Settings row for a ratio: the row prints it and draws it, and opens an editor with the ratios worth a name plus both sides always editable. It is kept as two numbers rather than as their quotient, since 16:9 is what a reader recognises.

Rows of the family go here
One under another

Inspector Group

The container the rows go in: a named, collapsible section that says what it is currently set to while it is closed, with an optional reset in its header. A drawer of twenty settings becomes a drawer of five groups.

Inspector Date

Settings row for a date, a time, or both: the row reads the value the way anyone would write it down and opens a real month calendar, while a time on its own keeps the platform's own field inline, since a time has no month to show.

Inspector Range

Settings row for a span rather than a value: two thumbs on the row itself, the band between them filling it, and both ends of the pair read out on the right. Ticks, tones and thumb states follow inspector-slider exactly, appearing on hover.