Getting StartedComponentsDesign TokensThemingTheme SynthesizerFrameworksAccessibilityUtilitiesServer RenderingBrowser SupportContributingChangelogApp ShellAspect GridAuth ShellCenterContainerDashboard GridFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbarAnchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkStepper NavTabsTop BarTree ViewAccordionAspect RatioAvatarAvatar GroupCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSkeletonSpinnerStackVideoVirtual ListActivity HeatmapAnimated NumberBadgeBar ListChartClockComparisonCountdown TimerData GridDescription ListDiffGaugeJSON TreeKanbanLevel MeterListMeterSparklineStatStepperTagTimelineUptimeValue CardWaveformBlockquoteCode BlockCode GroupGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriterButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerField ListFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree SelectAlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuHover CardLoading OverlayNotification PanelPopoverProgressSheetToastTooltip
ARC UIARC Radiant Components
v4.9DocsComponentsTokensSynthesizer
Getting StartedFrameworksServer RenderingDesign TokensThemingTheme SynthesizerTypographyUtilitiesAll ComponentsAccessibilityBrowser SupportChangelogContributingStatsApp ShellAspect GridAuth ShellCenterContainerDashboard GridFloat BarInsetMasonryPage HeaderPage LayoutResizableResponsive SwitcherSectionSettings LayoutSplit PaneStatus BarStickyToolbarAnchor NavBottom NavBreadcrumbBreadcrumb MenuCommand BarDrawerFooterLinkMenubarNavigation MenuPage IndicatorPaginationRailScroll IndicatorScroll SpyScroll To TopSidebarSkip LinkStepper NavTabsTop BarTree ViewAccordionAspect RatioAvatarAvatar GroupCardCarouselCollapsibleColor SwatchCTA BannerDividerEmpty StateFeature CardIconImageImage CompareImage HotspotsInfinite ScrollLightboxMarqueeQR CodeScroll AreaSkeletonSpinnerStackVideoVirtual ListActivity HeatmapAnimated NumberBadgeBar ListChartClockComparisonCountdown TimerData GridDescription ListDiffGaugeJSON TreeKanbanLevel MeterListMeterSparklineStatStepperTagTimelineUptimeValue CardWaveformBlockquoteCode BlockCode GroupGradient TextHighlightKbdKeyboard MapMarkdownNumber FormatProseTerminalTextTime AgoTruncateTypewriterButtonButton GroupCalendarCheckboxChipColor PickerComboboxCopy ButtonDate PickerDate Range PickerField ListFieldsetFile UploadFormHotkeyIcon ButtonImage CropperInline EditInputInput GroupKnobLabelMasked InputMulti SelectNumber InputPassword InputPin InputRadio GroupRange SliderRatingSearchSegmented ControlSelectSignature PadSliderSortable ListSwitch GroupTag InputTextareaTheme ToggleTime PickerToggleTransfer ListTree SelectAlertAnnouncementBannerCommand PaletteConfirmConnection StatusContext MenuConversationDialogDropdown MenuHover CardLoading OverlayNotification PanelPopoverProgressSheetToastTooltip

Pin Input

One-character-per-box input for PINs, OTPs, and verification codes with auto-advance, paste support, and optional masking.

ComponentsPin Input
inputinteractive
<arc-pin-input>

Overview

PinInput renders a row of individual input boxes, one per character, for entering PINs, one-time passwords, and verification codes. Each box accepts a single character and automatically advances focus to the next box on entry, so typing is fast. The component supports backspace navigation (moving back to the previous box when the current one is empty), arrow key movement between boxes, and full clipboard paste that fills multiple boxes at once. The `type` prop controls character validation: `"number"` restricts input to digits 0-9, `"alphanumeric"` allows letters and digits, and `"text"` accepts any single character. When `mask` is enabled, entered characters are obscured with dots (using CSS `-webkit-text-security: disc`) for sensitive codes. An optional `separator` prop inserts a visual dash between groups of boxes. For example, setting `separator="3"` on a 6-digit code renders it as three boxes, a dash, and three more boxes. PinInput fires `arc-input` on every character entry or deletion, providing the current partial value. When all boxes are filled it fires `arc-change` (the commit for a fixed-length code) along with `arc-complete`, the more specific name kept for consumers that auto-submit. Listen to either one to trigger form submission or validation at the right moment, with no polling or length-checking.

Guidelines

When to use

  • Set `length` to match the expected code length: 4 for PINs, 6 for OTPs, etc.
  • Use `type="number"` for numeric-only codes and set `inputmode="numeric"` for mobile keyboards
  • Enable `mask` for sensitive codes like passwords or security PINs
  • Listen for `arc-complete` to auto-submit or validate once the full code is entered
  • Provide a `label` so users understand what code they are entering

When not to use

  • Do not use PinInput for general text entry: it is designed exclusively for fixed-length codes
  • Do not set `length` higher than ~8: long codes are better handled with a standard text input
  • Do not omit the `label` prop when the pin input is used standalone without surrounding context
  • Do not use `separator` values that produce uneven groups at the end (e.g. `separator="4"` on a 6-digit code)
  • Avoid placing PinInput in very narrow containers: each box needs at least 42px width plus gaps

Features

  • Auto-advance focus to the next box after each valid character entry
  • Backspace navigates to and clears the previous box when the current box is empty
  • Arrow key navigation between boxes without modifying content
  • Clipboard paste support that fills multiple boxes from the cursor position
  • Configurable `type` validation: `"number"`, `"alphanumeric"`, or `"text"`
  • Mask mode via `mask` prop for obscuring sensitive codes with dots
  • Visual separator dashes between groups via the `separator` prop (e.g. every 3 boxes)
  • Split events: `arc-input` on every keystroke, `arc-change` and `arc-complete` when all boxes are filled

Preview

Usage

This component requires JavaScript. No pure HTML/CSS version is available. Use the Web Component directly or a framework wrapper.

<arc-pin-input label="OTP Code" length="6" separator="3"></arc-pin-input>

<script>
  document.querySelector('arc-pin-input')
    .addEventListener('arc-complete', e => {
      console.log('Code entered:', e.detail.value);
    });
</script>

API

valuestring''
Current combined value across all boxes. Reflected as an attribute.
namestring''
disabledbooleanfalse
Disables all boxes, reducing opacity to 40% and blocking input.
separatornumber0
Inserts a visual dash separator every N boxes. Set to 0 to disable separators.
labelstring''
Label text displayed above the input boxes in uppercase accent font.
lengthnumber4
Number of input boxes to render. Determines the expected code length.
type'number' | 'alphanumeric' | 'text''number'
Character validation mode. number allows digits only, alphanumeric allows letters and digits, text allows any character.
maskbooleanfalse
When true, obscures entered characters with dots for sensitive codes.
readonlybooleanfalse
Prevents entering, deleting, or pasting characters while the boxes stay focusable and the value still submits.
size'sm' | 'md' | 'lg''md'
Control size. md is the default; sm and lg scale the digit boxes.
formAssociatedbooleantrue
propertiesobject{ // flag(), unlike `disabled`. The exclusion in props.js is specifically // about form-associated *platform* semantics: a `disabled` content // attribute that is merely present makes the element actually disabled // per the HTML spec, and formDisabledCallback assigns the property back, // so no converter can win. Neither of these is platform-mapped: // `required` is enforced by _computeValidity() below and `readonly` by // each component's own interaction handlers, so the stock converter buys // nothing here and costs the usual bug: `required="false"` read as true, // blocking submission of a form the author meant to leave optional. // Finding #48's shape, across all 26 form controls at once. required: flag(false), readonly: flag(false), }
Lit merges static properties up the prototype chain, so every consumer gets these without declaring them. required participates in constraint validation below; readonly reflects for styling and is enforced by each component's interaction handlers (the mixin can't know which gestures mutate state).
autoValidatesbooleantrue
Components that run their own constraint-validation logic (pattern checks, range checks) opt out of the automatic required sync by overriding this to false, and own the whole validity flag set instead.
form
validity
validationMessage
requiredbooleanfalse

Methods

checkValidity()boolean
Whether the control currently satisfies its constraints, per the native constraint-validation API. Fires invalid on the element when it does not, and reports nothing to the user.
reportValidity()boolean
As checkValidity(), but also shows the browser's validation message against the control when it fails.

Events

arc-input
Fired on every character entry or deletion. event.detail.value contains the current partial value.
arc-change
Fired when the pin is complete: every box filled. That is the commit for a fixed-length value.
arc-complete
Fired alongside arc-change when all boxes are filled. The more specific name, kept for consumers that auto-submit.

See Also