Theme
Version
GitHub PyPI Discord
On this page

Pin input

Use CPinInput for one-time codes, PINs, and short recovery tokens. Its value is always a string, so a leading zero is preserved.

Enter a verification code

Give a standalone PinInput an accessible label, or place it in CField for a visible label, help, error, and shared state.

<c-CField required>
  <c-fill name="label">Verification code</c-fill>
  <c-fill name="description">Enter the six digits from your message.</c-fill>
  <c-fill name="default"><c-CPinInput name="code" /></c-fill>
</c-CField>
Enter a verification code
Show code
from citry import Component


class BasicPinInput(Component):
    class Kwargs:
        pass

    class Slots:
        pass

    template = """
      <c-CField required>
        <c-fill name="label">Verification code</c-fill>
        <c-fill name="description">Enter the six digits from your message.</c-fill>
        <c-fill name="default"><c-CPinInput name="code" /></c-fill>
      </c-CField>
    """


preview = BasicPinInput()
preview  # noqa: B018

One native text input owns focus, selection, paste, autofill, validation, and submission. The separate cells are visual only and create neither extra Tab stops nor separate Form values. Without JavaScript the native input remains a normal usable text box.

Accept recovery-code letters

The default type="numeric" accepts ASCII digits. Use alphabetic or alphanumeric for protocol tokens containing ASCII letters. These values are opaque identifiers, not localized numbers.

Enter an alphanumeric recovery code
Show code
from citry import Component


class AlphanumericPinInput(Component):
    class Kwargs:
        pass

    class Slots:
        pass

    template = """
      <c-CField>
        <c-fill name="label">Recovery code</c-fill>
        <c-fill name="description">Use the eight letters and digits printed with your account.</c-fill>
        <c-fill name="default"><c-CPinInput name="recovery" type="alphanumeric" c-length="8" /></c-fill>
      </c-CField>
    """


preview = AlphanumericPinInput()
preview  # noqa: B018

Invalid characters are discarded and reported through onValueInvalid. length is structural and supports 1 through 32 characters.

Control the value

Client value controls the exact string. An edit is a request: the displayed cells and Form value remain owner-controlled until the Alpine expression returns the requested value.

Control a PinInput
Show code
from citry import Component

# ruff: noqa: E501 - Alpine expression stays readable in public source


class ControlledPinInput(Component):
    class Kwargs:
        pass

    class Slots:
        pass

    template = """
      <section class="pin-input-demo-stack" x-data="{code:'12',last:'No request yet'}">
        <c-CPinInput
          label="Controlled four-digit code"
          value="12"
          c-length="4"
          $c-props="{value:code,onValueChange:(next,detail)=>{code=next;last=`${detail.source}: ${next}`},onComplete:(next)=>last=`Complete: ${next}`}"
        />
        <output x-text="last">No request yet</output>
        <c-CButton type="button" @click="code=''">Clear</c-CButton>
      </section>
    """
    css = ":where(.pin-input-demo-stack){display:grid;justify-items:start;gap:.75rem}"


preview = ControlledPinInput()
preview  # noqa: B018

onValueChange reports accepted edits. onComplete reports a transition to a full value and never submits the Form automatically. Paste and autofill remain available.

Preserve native Form behavior

required combines with an exact-length native pattern, so an empty or partial required code blocks submission. Readonly values remain focusable and submit; disabled values do not submit.

Submit and reset codes
Show code
from citry import Component

# ruff: noqa: E501 - template expression stays readable in public source


class PinInputForms(Component):
    class Kwargs:
        pass

    class Slots:
        pass

    template = """
      <form class="pin-input-demo-stack" x-data="{result:'Submit or reset the Form'}" @submit.prevent="result=JSON.stringify(Array.from(new FormData($event.target).entries()))">
        <c-CField required>
          <c-fill name="label">One-time code</c-fill>
          <c-fill name="default"><c-CPinInput name="code" value="01" /></c-fill>
        </c-CField>
        <c-CPinInput name="issued" label="Issued code" value="246810" readonly />
        <c-CGroup><c-CButton type="submit">Submit</c-CButton><c-CButton type="reset" variant="outline">Reset</c-CButton></c-CGroup>
        <output x-text="result">Submit or reset the Form</output>
      </form>
    """
    css = ":where(.pin-input-demo-stack){display:grid;justify-items:start;gap:1rem}"


preview = PinInputForms()
preview  # noqa: B018

one_time_code=True emits autocomplete="one-time-code". Set an explicit input_attrs={"autocomplete": "..."} when another autocomplete policy is required. Citry never invokes WebOTP or reads SMS messages.

Mask or group the visual cells

mask=True replaces filled visual cells with bullets without changing the submitted string. It reduces shoulder surfing but is not encryption and does not hide the accessible text-field value from assistive software.

Mask a code
Show code
from citry import Component


class MaskedPinInput(Component):
    class Kwargs:
        pass

    class Slots:
        pass

    template = """
      <section class="pin-input-demo-stack">
        <c-CPinInput label="Private access code" name="access-code" value="7412" c-length="4" mask />
        <p>Masking changes the visual cells only. Treat the submitted token as sensitive data.</p>
      </section>
    """
    css = ":where(.pin-input-demo-stack){display:grid;justify-items:start;gap:.75rem}"


preview = MaskedPinInput()
preview  # noqa: B018

Use attached=True to join cells. For a 3–3 presentation, provide separator_after=(2,) and the separator slot. Separator output is visual; put instructions in Field description text.

Group code cells
Show code
from citry import Component


class SeparatedPinInput(Component):
    class Kwargs:
        pass

    class Slots:
        pass

    template = """
      <section class="pin-input-demo-stack">
        <c-CPinInput label="Grouped recovery code" type="alphanumeric" c-separator_after="(2,)">
          <c-fill name="separator" data="{ index }">-</c-fill>
        </c-CPinInput>
        <c-CPinInput label="Attached four-digit code" c-length="4" attached />
      </section>
    """
    css = ":where(.pin-input-demo-stack){display:grid;justify-items:start;gap:1rem}"


preview = SeparatedPinInput()
preview  # noqa: B018

Keep code direction and locale ownership clear

PinInput renders protocol tokens left-to-right by default, including inside an RTL page. ASCII digits are not localized. Labels, Field text, placeholders, and separators belong to the application and stay in its locale.

Use PinInput in RTL content
Show code
from citry import Component


class PinInputLocales(Component):
    class Kwargs:
        pass

    class Slots:
        pass

    template = """
      <section class="pin-input-demo-stack" dir="rtl">
        <c-CField>
          <c-fill name="label">Ψ±Ω…Ψ² Ψ§Ω„ΨͺΨ­Ω‚Ω‚</c-fill>
          <c-fill name="description">ΩŠΨ¨Ω‚Ω‰ Ψ±Ω…Ψ² Ψ§Ω„Ψ¨Ψ±ΩˆΨͺΩˆΩƒΩˆΩ„ Ω…Ω† Ψ§Ω„ΩŠΨ³Ψ§Ψ± Ψ₯Ω„Ω‰ Ψ§Ω„ΩŠΩ…ΩŠΩ†.</c-fill>
          <c-fill name="default"><c-CPinInput value="104" /></c-fill>
        </c-CField>
      </section>
    """
    css = ":where(.pin-input-demo-stack){display:grid;justify-items:start;gap:.75rem}"


preview = PinInputLocales()
preview  # noqa: B018

Choose states and public styles

Outline and subtle variants combine with sm, md, and lg sizes. Public --cui-pin-input-* variables and documented part selectors customize cells, focus, separators, and state treatment.

Compare PinInput states
Show code
from citry import Component


class PinInputStates(Component):
    class Kwargs:
        pass

    class Slots:
        pass

    template = """
      <section class="pin-input-state-grid">
        <c-CPinInput label="Small subtle code" value="12" size="sm" variant="subtle" />
        <c-CPinInput label="Default code" value="123" />
        <c-CPinInput label="Large complete code" value="123456" size="lg" />
        <c-CPinInput label="Readonly code" value="246810" readonly />
        <c-CPinInput label="Disabled code" value="135790" disabled />
        <c-CPinInput label="Invalid code" value="12" invalid class_="pin-input-brand" />
      </section>
    """
    css = """
      :where(.pin-input-state-grid){display:grid;grid-template-columns:repeat(auto-fit,minmax(18rem,1fr));gap:1.5rem;align-items:start}
      :where(.pin-input-brand){--cui-pin-input-focus-color:#7c3aed;--cui-pin-input-radius:.75rem}
    """


preview = PinInputStates()
preview  # noqa: B018

Tab enters the component once. Native text editing and clipboard shortcuts continue to work; Home, End, and pointer selection move the active visual cell.

API reference

Inputs

CPinInput server inputs

Server inputs are passed in a template through <c-CPinInput ... /> or in Python through CPinInput(...).

InputTypeDefaultEffect
valuestr""Sets the initial exact string and preserves leading zeroes.
namestr | NoneNoneSets the native Form field name.
formstr | NoneNoneAssociates the native input with an external Form ID.
idstr | NonegeneratedSets the native input ID and bases the root ID.
lengthint6Sets one through thirty-two characters cells maxlength and exact validity.
typeCPinInputType (CPinInputType)"numeric"Chooses the ASCII numeric alphabetic or alphanumeric token alphabet.
requiredbool | NoneNoneEnables exact-length native required validity outside Field.
disabledbool | NoneNoneBlocks focus edits and Form submission outside Field.
readonlybool | NoneNonePreserves focus selection and submission while blocking edits outside Field.
invalidbool | NoneNoneReflects application invalid state outside Field.
maskboolFalseReplaces filled visual cells with bullets without changing the value.
one_time_codeboolTrueEmits one-time-code autocomplete unless input_attrs supplies another token.
placeholderone-code-point str | None"β—‹"Supplies the caller-authored empty-cell marker.
attachedboolFalseJoins adjacent visual cells.
separator_afterSequence[int] | NoneNoneSelects zero-based boundaries after which the separator slot renders.
labelstr | NoneNoneNames a standalone native input; use the Field label slot inside Field.
sizeCPinInputSize (CPinInputSize)"md"Selects coordinated cell sizing.
variantCPinInputVariant (CPinInputVariant)"outline"Selects cell surface treatment.
class_CClassValue | None (CClassValue)NoneAdds classes to the root and merges with attrs.
styleCStyleValue | None (CStyleValue)NoneAdds styles to the root and merges with attrs.
attrsMapping[str, object] | NoneNoneAdds copied allowed root attributes without replacing owned state or identity.
input_attrsMapping[str, object] | NoneNoneAdds copied native attributes including accessible naming descriptions autocomplete and dir without replacing owned behavior.

CPinInput client inputs

Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CPinInput />.

InputTypeOmitted behaviorEffect
valuestringReleases control to the last uncontrolled value.Controls the exact accepted token string.
requiredbooleanUses server or Field state.Controls standalone exact-length validity.
disabledbooleanUses server or owner state.Controls editing focus and Form participation.
readonlybooleanUses server or owner state.Controls focusable nonmutable submission.
invalidbooleanUses server or Field state.Controls application invalid state.
maskbooleanUses the server value.Controls visual masking.
variantCPinInputVariant (CPinInputVariant)Uses the server value.Controls surface treatment.
sizeCPinInputSize (CPinInputSize)Uses the server value.Controls coordinated sizing.
onValueChangefunctionNo value callback.Receives each accepted user edit or reset request.
onCompletefunctionNo completion callback.Receives transitions to a complete accepted token.
onValueInvalidfunctionNo rejection callback.Receives discarded characters and their input source.
onFocusChangefunctionNo focus callback.Receives native focus entry and exit.

Slots

Slots are passed as nested content or <c-fill> tags in a template, or through the slots={...} argument in Python.

CPinInput slots

SlotRequiredDataFallback
separatorno{index: int} (CPinInputSeparatorSlotData)No visual content at each separator_after boundary.

Events

Component events are callback inputs supplied through $c-props. Native browser events remain available through Alpine @... attributes.

CPinInput events

EventSignatureTrigger and timingDetailControlled and cancellation behavior
onValueChange(value: string, detail: CPinInputValueChangeDetail) => void (CPinInputValueChangeDetail)An accepted edit or Form reset requests a different value.{value, previousValue, controlled, source, sourceEvent} (CPinInputValueChangeDetail)Uncontrolled state commits first; controlled state is request-only.
onComplete(value: string, detail: CPinInputCompleteDetail) => void (CPinInputCompleteDetail)Accepted input transitions to the exact configured length.{value, controlled, source, sourceEvent} (CPinInputCompleteDetail)Reports completion without automatically submitting.
onValueInvalid(detail: CPinInputInvalidDetail) => void (CPinInputInvalidDetail)One edit contains disallowed or overflow characters.{value, rejected, source, sourceEvent} (CPinInputInvalidDetail)Reports plain rejected text after filtering it from the value.
onFocusChange(focused: boolean, detail: CPinInputFocusChangeDetail) => void (CPinInputFocusChangeDetail)The native text input focuses or blurs.{focused, sourceEvent} (CPinInputFocusChangeDetail)Runs after focus reflection changes.

Methods

-

CSS

CSS variables to theme the components. Set them on an ancestor or the component itself.

CPinInput CSS variables

Apply these variables to CPinInput or one of its ancestors.

VariableTypePurposeDefault
--cui-pin-input-cell-sizelengthVisual cell inline and block size.Size-dependent 2.75rem.
--cui-pin-input-gaplengthSpace between separate cells.0.5rem
--cui-pin-input-separator-gaplengthExtra space for a separator boundary.0.4rem
--cui-pin-input-border-colorcolorOutline cell border.Mixed CanvasText.
--cui-pin-input-focus-colorcolorActive-cell focus ring.Highlight
--cui-pin-input-invalid-colorcolorInvalid border treatment.Theme danger color.
--cui-pin-input-backgroundcolorCell surface.Canvas
--cui-pin-input-colorcolorEntered character color.CanvasText
--cui-pin-input-placeholder-colorcolorEmpty-cell marker color.Muted CanvasText.
--cui-pin-input-radiuslengthCell corner radius.0.5rem
--cui-pin-input-disabled-opacitynumberDisabled treatment opacity.0.58

Attributes

HTML attributes defined on the components that you can refer to for CSS, inspection, and testing. Read-only.

CPinInput attributes

AttributeElementTypeMeaning
data-requiredRoot divpresent | absentMirrors effective requiredness.
data-disabledRoot divpresent | absentMirrors effective disabledness.
data-readonlyRoot divpresent | absentMirrors effective readonly state.
data-invalidRoot divpresent | absentMirrors effective application or native invalidity.
data-focusedRoot divpresent | absentMarks native input focus.
data-filledRoot divpresent | absentMarks any accepted character.
data-completeRoot divpresent | absentMarks an accepted exact-length value.
data-attachedRoot divpresent | absentMarks joined cell styling.
data-variantRoot divCPinInputVariant (CPinInputVariant)Mirrors surface treatment.
data-sizeRoot divCPinInputSize (CPinInputSize)Mirrors coordinated sizing.

CPinInput attributes

AttributeElementTypeMeaning
data-activeVisual cellpresent | absentMarks the logical native selection or insertion cell.
data-filledVisual cellpresent | absentMarks an accepted character.
data-maskedVisual cellpresent | absentMarks a character currently displayed as a bullet.

Selectors

Selectors for the DOM nodes in the components that you can use for CSS, inspection, and testing.

CPinInput selectors

SelectorElementPurpose
[data-citry-ui-part="pin-input"]Root divState reflections and root customization destination.
[data-citry-ui-part="input"]Native text inputOwns semantics focus editing validation Form value and input_attrs.
[data-citry-ui-part="cells"]Aria-hidden presentation spanContains the segmented visual display.
[data-citry-ui-part="cell"]Visual cell spanDisplays one accepted position and receives pointer selection.
[data-citry-ui-part="character"]Character spanDisplays entered masked or placeholder content.
[data-citry-ui-part="caret"]Decorative caret spanMarks an active empty insertion cell.
[data-citry-ui-part="separator"]Visual separator spanHosts the caller separator slot at selected boundaries.

Interfaces

Aliases and data shapes referenced above.

Input type aliases

InterfaceDefinition
CPinInputTypeLiteral["numeric", "alphabetic", "alphanumeric"]
CPinInputSizeLiteral["sm", "md", "lg"]
CPinInputVariantLiteral["outline", "subtle"]
CPinInputChangeSourceLiteral["input", "paste", "autofill", "composition", "reset"]
CPinInputInvalidSourceLiteral["input", "paste", "autofill", "composition"]
CClassValuestr | Mapping[str, bool] | Sequence[CClassValue]
CStyleValuestr | Mapping[str, object] | Sequence[CStyleValue]

CPinInputSeparatorSlotData

FieldTypeDefaultMeaning
indexint-Zero-based cell index after which this separator renders.

CPinInputValueChangeDetail

FieldTypeDefaultMeaning
valuestring-Requested accepted token.
previousValuestring-Effective token before the request.
controlledboolean-Whether client value owns committed state.
sourceCPinInputChangeSource (CPinInputChangeSource)-Edit or reset source.
sourceEventobject | null-Native event when one exists.

CPinInputCompleteDetail

FieldTypeDefaultMeaning
valuestring-Complete accepted token.
controlledboolean-Whether client value owns committed state.
sourceCPinInputChangeSource (CPinInputChangeSource)-Completion source.
sourceEventobject | null-Native event.

CPinInputInvalidDetail

FieldTypeDefaultMeaning
valuestring-Accepted filtered token.
rejectedstring-Discarded plain characters.
sourceCPinInputInvalidSource (CPinInputInvalidSource)-Rejected edit source.
sourceEventobject | null-Native input event.

CPinInputFocusChangeDetail

FieldTypeDefaultMeaning
focusedboolean-Current native focus state.
sourceEventobject-Native focus or blur event.

Translation keys

-