Native Select and Input OTP
Native Select and Input OTP: anatomy, keyboard behavior, state, and theming in Askr.
Example
import { InputOTP, InputOTPGroup, InputOTPSlot, NativeSelect } from '@askrjs/themes/components';
<>
<NativeSelect name="timezone" aria-label="Timezone">
<option value="America/New_York">Eastern time</option>
</NativeSelect>
<InputOTP maxLength={6} value={code()} onValueChange={setCode}>
<InputOTPGroup>{[0, 1, 2, 3, 4, 5].map((index) => <InputOTPSlot index={index} />)}</InputOTPGroup>
</InputOTP>
</>Purpose
Native Select wraps a real <select> element; Input OTP wraps a set of generic, untyped slot elements meant to look like a one-time-passcode input. They land in the same experimental bucket for different reasons — Native Select is genuinely close to finished because the browser does most of the work, while Input OTP is closer to a visual mockup than a working input.
Install and import
Import NativeSelect from @askrjs/themes/native-select, and InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator from @askrjs/themes/input-otp. Both resolve to the same shared components barrel as the rest of @askrjs/themes, so the subpath is a naming convention, not a separately built module.
Live examples
NativeSelect works as a drop-in example immediately — render it with <option> children and you have a fully functional, accessible select with zero extra code. InputOTP does not: InputOTPSlot renders a <span>, not an <input>, so a real example needs you to layer actual focusable inputs (or a hidden text input plus visual slots) on top of the markup this component provides.
Anatomy
NativeSelect renders a single native <select data-slot="native-select"> and accepts children directly, so you populate it with plain <option> elements just as you would outside any framework. InputOTP (role="group") contains InputOTPGroup (role="group") holding a row of InputOTPSlot elements, optionally separated by InputOTPSeparator, which defaults to rendering a "-" character marked aria-hidden="true".
State model
NativeSelect has no framework-level state — it's a real <select>, so its value lives in the DOM and in whatever onChange/value props you spread onto it, identical to any native form control. InputOTP has no state at all: InputOTPSlot doesn't track a character, a focus index, or a completion flag, so tracking which slot is active and what digit it holds is code you write from scratch.
Keyboard and accessibility
NativeSelect gets full native keyboard support — arrow keys, type-ahead, Space/Enter to open — for free, because it's an unmodified <select> element. InputOTP gets none of that: since its slots are <span>s rather than focusable inputs, there's no built-in tab order, no auto-advance to the next slot on keypress, and no backspace-to-previous-slot behavior; all of it has to be implemented by whatever real input elements you place inside the slots.
Styling and tokens
NativeSelect applies the shared input class plus data-slot="native-select", so it matches the visual weight of Input and other form fields without a custom native-select skin. InputOTP's parts each carry their own data-slot (input-otp, input-otp-group, input-otp-slot, input-otp-separator), giving you hooks to style the slot boxes and separators independently.
API
NativeSelect accepts the generic CatalogComponentProps shape plus native <select> attributes — there's no NativeSelectProps type restricting value or onChange beyond what a <select> already supports. Every InputOTP part is likewise typed only as CatalogComponentProps, with no length, value, or onComplete prop — the kinds of APIs a finished OTP input would need, and none of them exist yet.
Edge cases
For NativeSelect, styling a native <select>'s dropdown popup consistently across browsers is a platform limitation you inherit, not an Askr gap — don't expect token overrides to reach into the OS-rendered option list. For InputOTP, treat it as scaffolding: shipping it as-is without adding real inputs, focus management, and paste-splitting (so pasting a full code fills every slot) produces a control that looks right but isn't usable by keyboard or screen reader.
Related pages
See Combobox and Command for the other themes-only experimental family that ships markup without behavior. See Structures and Forms and controls for the supported, behavior-complete input components to reach for when a real product surface needs a form control today.