Pin Input
OTP / 認証コード入力 — N 個の単一文字ボックスが auto-advance / backspace-back / arrow nav / paste-to-fill で連携。autocomplete="one-time-code" を最初のボックスに付与済 (iOS/Android で SMS コード候補が出る)。numeric (default) / alphanumeric、length / size / masked / autofocus 等の prop。combined value は hidden input で form submit される。
最小 — 6 桁 OTP (default)
Enter the 6-digit code we sent you
<x-pin-input name="otp" label="Verification code" hint="Enter the 6-digit code we sent you" />
<div class="w-full" x-data="{ digits: Array.from({ length: 6 }, (_, i) => ([])[i] ?? ''), get combined() { return this.digits.join(''); }, normalize(c) { if (!c) return ''; const ch = c.slice(-1); return /[0-9]/.test(ch) ? ch : ''; }, onInput(idx, e) { const ch = this.normalize(e.target.value); this.digits[idx] = ch; if (ch && idx < this.digits.length - 1) { this.$nextTick(() => this.$refs['pin' + (idx + 1)]?.focus()); } }, onKeydown(idx, e) { if (e.key === 'Backspace' && !this.digits[idx] && idx > 0) { this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowLeft' && idx > 0) { e.preventDefault(); this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowRight' && idx < this.digits.length - 1) { e.preventDefault(); this.$refs['pin' + (idx + 1)]?.focus(); } }, onPaste(e) { e.preventDefault(); const txt = (e.clipboardData?.getData('text') || '').trim(); const chars = Array.from(txt).map(c => this.normalize(c)).filter(c => c); for (let i = 0; i < this.digits.length; i++) { this.digits[i] = chars[i] ?? ''; } let lastFilled = -1; for (let i = 0; i < this.digits.length; i++) { if (this.digits[i]) lastFilled = i; } const target = lastFilled === this.digits.length - 1 ? lastFilled : Math.min(lastFilled + 1, this.digits.length - 1); if (target >= 0) this.$nextTick(() => this.$refs['pin' + target]?.focus()); }, }">
<label class="block mb-1.5 text-[length:var(--text-field-sm)] font-medium text-base-content">Verification code</label>
<div class="flex items-center gap-2">
<input type="text" x-model="digits[0]" x-ref="pin0" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="one-time-code" x-on:input="onInput(0, $event)" x-on:keydown="onKeydown(0, $event)" x-on:paste="onPaste($event)" aria-label="Digit 1" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[1]" x-ref="pin1" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(1, $event)" x-on:keydown="onKeydown(1, $event)" aria-label="Digit 2" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[2]" x-ref="pin2" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(2, $event)" x-on:keydown="onKeydown(2, $event)" aria-label="Digit 3" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[3]" x-ref="pin3" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(3, $event)" x-on:keydown="onKeydown(3, $event)" aria-label="Digit 4" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[4]" x-ref="pin4" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(4, $event)" x-on:keydown="onKeydown(4, $event)" aria-label="Digit 5" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[5]" x-ref="pin5" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(5, $event)" x-on:keydown="onKeydown(5, $event)" aria-label="Digit 6" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
</div><input type="hidden" name="otp" x-bind:value="combined">
<p class="mt-1.5 text-[length:var(--text-field-sm)] text-base-content/50">
Enter the 6-digit code we sent you
</p>
</div>
4 桁 PIN を masked で
Digits are masked as you type
<x-pin-input name="pin" label="PIN" :length="4" masked hint="Digits are masked as you type" />
<div class="w-full" x-data="{ digits: Array.from({ length: 4 }, (_, i) => ([])[i] ?? ''), get combined() { return this.digits.join(''); }, normalize(c) { if (!c) return ''; const ch = c.slice(-1); return /[0-9]/.test(ch) ? ch : ''; }, onInput(idx, e) { const ch = this.normalize(e.target.value); this.digits[idx] = ch; if (ch && idx < this.digits.length - 1) { this.$nextTick(() => this.$refs['pin' + (idx + 1)]?.focus()); } }, onKeydown(idx, e) { if (e.key === 'Backspace' && !this.digits[idx] && idx > 0) { this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowLeft' && idx > 0) { e.preventDefault(); this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowRight' && idx < this.digits.length - 1) { e.preventDefault(); this.$refs['pin' + (idx + 1)]?.focus(); } }, onPaste(e) { e.preventDefault(); const txt = (e.clipboardData?.getData('text') || '').trim(); const chars = Array.from(txt).map(c => this.normalize(c)).filter(c => c); for (let i = 0; i < this.digits.length; i++) { this.digits[i] = chars[i] ?? ''; } let lastFilled = -1; for (let i = 0; i < this.digits.length; i++) { if (this.digits[i]) lastFilled = i; } const target = lastFilled === this.digits.length - 1 ? lastFilled : Math.min(lastFilled + 1, this.digits.length - 1); if (target >= 0) this.$nextTick(() => this.$refs['pin' + target]?.focus()); }, }">
<label class="block mb-1.5 text-[length:var(--text-field-sm)] font-medium text-base-content">PIN</label>
<div class="flex items-center gap-2">
<input type="password" x-model="digits[0]" x-ref="pin0" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="one-time-code" x-on:input="onInput(0, $event)" x-on:keydown="onKeydown(0, $event)" x-on:paste="onPaste($event)" aria-label="Digit 1" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="password" x-model="digits[1]" x-ref="pin1" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(1, $event)" x-on:keydown="onKeydown(1, $event)" aria-label="Digit 2" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="password" x-model="digits[2]" x-ref="pin2" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(2, $event)" x-on:keydown="onKeydown(2, $event)" aria-label="Digit 3" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="password" x-model="digits[3]" x-ref="pin3" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(3, $event)" x-on:keydown="onKeydown(3, $event)" aria-label="Digit 4" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
</div><input type="hidden" name="pin" x-bind:value="combined">
<p class="mt-1.5 text-[length:var(--text-field-sm)] text-base-content/50">
Digits are masked as you type
</p>
</div>
8 文字の英数字招待コード
Letters and digits, case-insensitive
<x-pin-input name="invite" label="Invite code" :length="8" type="alphanumeric" size="sm" hint="Letters and digits, case-insensitive" />
<div class="w-full" x-data="{ digits: Array.from({ length: 8 }, (_, i) => ([])[i] ?? ''), get combined() { return this.digits.join(''); }, normalize(c) { if (!c) return ''; const ch = c.slice(-1); return /[0-9a-zA-Z]/.test(ch) ? ch : ''; }, onInput(idx, e) { const ch = this.normalize(e.target.value); this.digits[idx] = ch; if (ch && idx < this.digits.length - 1) { this.$nextTick(() => this.$refs['pin' + (idx + 1)]?.focus()); } }, onKeydown(idx, e) { if (e.key === 'Backspace' && !this.digits[idx] && idx > 0) { this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowLeft' && idx > 0) { e.preventDefault(); this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowRight' && idx < this.digits.length - 1) { e.preventDefault(); this.$refs['pin' + (idx + 1)]?.focus(); } }, onPaste(e) { e.preventDefault(); const txt = (e.clipboardData?.getData('text') || '').trim(); const chars = Array.from(txt).map(c => this.normalize(c)).filter(c => c); for (let i = 0; i < this.digits.length; i++) { this.digits[i] = chars[i] ?? ''; } let lastFilled = -1; for (let i = 0; i < this.digits.length; i++) { if (this.digits[i]) lastFilled = i; } const target = lastFilled === this.digits.length - 1 ? lastFilled : Math.min(lastFilled + 1, this.digits.length - 1); if (target >= 0) this.$nextTick(() => this.$refs['pin' + target]?.focus()); }, }">
<label class="block mb-1.5 text-[length:var(--text-field-sm)] font-medium text-base-content">Invite code</label>
<div class="flex items-center gap-2">
<input type="text" x-model="digits[0]" x-ref="pin0" pattern="[0-9a-zA-Z]" inputmode="text" maxlength="1" autocomplete="one-time-code" x-on:input="onInput(0, $event)" x-on:keydown="onKeydown(0, $event)" x-on:paste="onPaste($event)" aria-label="Digit 1" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[1]" x-ref="pin1" pattern="[0-9a-zA-Z]" inputmode="text" maxlength="1" autocomplete="off" x-on:input="onInput(1, $event)" x-on:keydown="onKeydown(1, $event)" aria-label="Digit 2" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[2]" x-ref="pin2" pattern="[0-9a-zA-Z]" inputmode="text" maxlength="1" autocomplete="off" x-on:input="onInput(2, $event)" x-on:keydown="onKeydown(2, $event)" aria-label="Digit 3" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[3]" x-ref="pin3" pattern="[0-9a-zA-Z]" inputmode="text" maxlength="1" autocomplete="off" x-on:input="onInput(3, $event)" x-on:keydown="onKeydown(3, $event)" aria-label="Digit 4" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[4]" x-ref="pin4" pattern="[0-9a-zA-Z]" inputmode="text" maxlength="1" autocomplete="off" x-on:input="onInput(4, $event)" x-on:keydown="onKeydown(4, $event)" aria-label="Digit 5" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[5]" x-ref="pin5" pattern="[0-9a-zA-Z]" inputmode="text" maxlength="1" autocomplete="off" x-on:input="onInput(5, $event)" x-on:keydown="onKeydown(5, $event)" aria-label="Digit 6" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[6]" x-ref="pin6" pattern="[0-9a-zA-Z]" inputmode="text" maxlength="1" autocomplete="off" x-on:input="onInput(6, $event)" x-on:keydown="onKeydown(6, $event)" aria-label="Digit 7" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[7]" x-ref="pin7" pattern="[0-9a-zA-Z]" inputmode="text" maxlength="1" autocomplete="off" x-on:input="onInput(7, $event)" x-on:keydown="onKeydown(7, $event)" aria-label="Digit 8" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
</div><input type="hidden" name="invite" x-bind:value="combined">
<p class="mt-1.5 text-[length:var(--text-field-sm)] text-base-content/50">
Letters and digits, case-insensitive
</p>
</div>
初期値セット
The remaining cells stay empty and focusable
<x-pin-input name="otp_prefilled" label="Code" :length="6" value="123" hint="The remaining cells stay empty and focusable" />
<div class="w-full" x-data="{ digits: Array.from({ length: 6 }, (_, i) => (["1","2","3"])[i] ?? ''), get combined() { return this.digits.join(''); }, normalize(c) { if (!c) return ''; const ch = c.slice(-1); return /[0-9]/.test(ch) ? ch : ''; }, onInput(idx, e) { const ch = this.normalize(e.target.value); this.digits[idx] = ch; if (ch && idx < this.digits.length - 1) { this.$nextTick(() => this.$refs['pin' + (idx + 1)]?.focus()); } }, onKeydown(idx, e) { if (e.key === 'Backspace' && !this.digits[idx] && idx > 0) { this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowLeft' && idx > 0) { e.preventDefault(); this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowRight' && idx < this.digits.length - 1) { e.preventDefault(); this.$refs['pin' + (idx + 1)]?.focus(); } }, onPaste(e) { e.preventDefault(); const txt = (e.clipboardData?.getData('text') || '').trim(); const chars = Array.from(txt).map(c => this.normalize(c)).filter(c => c); for (let i = 0; i < this.digits.length; i++) { this.digits[i] = chars[i] ?? ''; } let lastFilled = -1; for (let i = 0; i < this.digits.length; i++) { if (this.digits[i]) lastFilled = i; } const target = lastFilled === this.digits.length - 1 ? lastFilled : Math.min(lastFilled + 1, this.digits.length - 1); if (target >= 0) this.$nextTick(() => this.$refs['pin' + target]?.focus()); }, }">
<label class="block mb-1.5 text-[length:var(--text-field-sm)] font-medium text-base-content">Code</label>
<div class="flex items-center gap-2">
<input type="text" x-model="digits[0]" x-ref="pin0" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="one-time-code" x-on:input="onInput(0, $event)" x-on:keydown="onKeydown(0, $event)" x-on:paste="onPaste($event)" aria-label="Digit 1" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[1]" x-ref="pin1" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(1, $event)" x-on:keydown="onKeydown(1, $event)" aria-label="Digit 2" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[2]" x-ref="pin2" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(2, $event)" x-on:keydown="onKeydown(2, $event)" aria-label="Digit 3" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[3]" x-ref="pin3" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(3, $event)" x-on:keydown="onKeydown(3, $event)" aria-label="Digit 4" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[4]" x-ref="pin4" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(4, $event)" x-on:keydown="onKeydown(4, $event)" aria-label="Digit 5" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[5]" x-ref="pin5" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(5, $event)" x-on:keydown="onKeydown(5, $event)" aria-label="Digit 6" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
</div><input type="hidden" name="otp_prefilled" x-bind:value="combined">
<p class="mt-1.5 text-[length:var(--text-field-sm)] text-base-content/50">
The remaining cells stay empty and focusable
</p>
</div>
size — xs / sm / md (default) / lg
<x-pin-input size="xs" :length="4" />
<x-pin-input size="sm" :length="4" />
<x-pin-input size="md" :length="4" />
<x-pin-input size="lg" :length="4" />
<div class="w-full" x-data="{ digits: Array.from({ length: 4 }, (_, i) => ([])[i] ?? ''), get combined() { return this.digits.join(''); }, normalize(c) { if (!c) return ''; const ch = c.slice(-1); return /[0-9]/.test(ch) ? ch : ''; }, onInput(idx, e) { const ch = this.normalize(e.target.value); this.digits[idx] = ch; if (ch && idx < this.digits.length - 1) { this.$nextTick(() => this.$refs['pin' + (idx + 1)]?.focus()); } }, onKeydown(idx, e) { if (e.key === 'Backspace' && !this.digits[idx] && idx > 0) { this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowLeft' && idx > 0) { e.preventDefault(); this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowRight' && idx < this.digits.length - 1) { e.preventDefault(); this.$refs['pin' + (idx + 1)]?.focus(); } }, onPaste(e) { e.preventDefault(); const txt = (e.clipboardData?.getData('text') || '').trim(); const chars = Array.from(txt).map(c => this.normalize(c)).filter(c => c); for (let i = 0; i < this.digits.length; i++) { this.digits[i] = chars[i] ?? ''; } let lastFilled = -1; for (let i = 0; i < this.digits.length; i++) { if (this.digits[i]) lastFilled = i; } const target = lastFilled === this.digits.length - 1 ? lastFilled : Math.min(lastFilled + 1, this.digits.length - 1); if (target >= 0) this.$nextTick(() => this.$refs['pin' + target]?.focus()); }, }">
<div class="flex items-center gap-2">
<input type="text" x-model="digits[0]" x-ref="pin0" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="one-time-code" x-on:input="onInput(0, $event)" x-on:keydown="onKeydown(0, $event)" x-on:paste="onPaste($event)" aria-label="Digit 1" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-8 h-[var(--h-field-xs)] text-base tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[1]" x-ref="pin1" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(1, $event)" x-on:keydown="onKeydown(1, $event)" aria-label="Digit 2" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-8 h-[var(--h-field-xs)] text-base tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[2]" x-ref="pin2" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(2, $event)" x-on:keydown="onKeydown(2, $event)" aria-label="Digit 3" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-8 h-[var(--h-field-xs)] text-base tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[3]" x-ref="pin3" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(3, $event)" x-on:keydown="onKeydown(3, $event)" aria-label="Digit 4" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-8 h-[var(--h-field-xs)] text-base tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
</div>
</div>
<div class="w-full" x-data="{ digits: Array.from({ length: 4 }, (_, i) => ([])[i] ?? ''), get combined() { return this.digits.join(''); }, normalize(c) { if (!c) return ''; const ch = c.slice(-1); return /[0-9]/.test(ch) ? ch : ''; }, onInput(idx, e) { const ch = this.normalize(e.target.value); this.digits[idx] = ch; if (ch && idx < this.digits.length - 1) { this.$nextTick(() => this.$refs['pin' + (idx + 1)]?.focus()); } }, onKeydown(idx, e) { if (e.key === 'Backspace' && !this.digits[idx] && idx > 0) { this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowLeft' && idx > 0) { e.preventDefault(); this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowRight' && idx < this.digits.length - 1) { e.preventDefault(); this.$refs['pin' + (idx + 1)]?.focus(); } }, onPaste(e) { e.preventDefault(); const txt = (e.clipboardData?.getData('text') || '').trim(); const chars = Array.from(txt).map(c => this.normalize(c)).filter(c => c); for (let i = 0; i < this.digits.length; i++) { this.digits[i] = chars[i] ?? ''; } let lastFilled = -1; for (let i = 0; i < this.digits.length; i++) { if (this.digits[i]) lastFilled = i; } const target = lastFilled === this.digits.length - 1 ? lastFilled : Math.min(lastFilled + 1, this.digits.length - 1); if (target >= 0) this.$nextTick(() => this.$refs['pin' + target]?.focus()); }, }">
<div class="flex items-center gap-2">
<input type="text" x-model="digits[0]" x-ref="pin0" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="one-time-code" x-on:input="onInput(0, $event)" x-on:keydown="onKeydown(0, $event)" x-on:paste="onPaste($event)" aria-label="Digit 1" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[1]" x-ref="pin1" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(1, $event)" x-on:keydown="onKeydown(1, $event)" aria-label="Digit 2" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[2]" x-ref="pin2" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(2, $event)" x-on:keydown="onKeydown(2, $event)" aria-label="Digit 3" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[3]" x-ref="pin3" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(3, $event)" x-on:keydown="onKeydown(3, $event)" aria-label="Digit 4" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-10 h-[var(--h-field-sm)] text-lg tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
</div>
</div>
<div class="w-full" x-data="{ digits: Array.from({ length: 4 }, (_, i) => ([])[i] ?? ''), get combined() { return this.digits.join(''); }, normalize(c) { if (!c) return ''; const ch = c.slice(-1); return /[0-9]/.test(ch) ? ch : ''; }, onInput(idx, e) { const ch = this.normalize(e.target.value); this.digits[idx] = ch; if (ch && idx < this.digits.length - 1) { this.$nextTick(() => this.$refs['pin' + (idx + 1)]?.focus()); } }, onKeydown(idx, e) { if (e.key === 'Backspace' && !this.digits[idx] && idx > 0) { this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowLeft' && idx > 0) { e.preventDefault(); this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowRight' && idx < this.digits.length - 1) { e.preventDefault(); this.$refs['pin' + (idx + 1)]?.focus(); } }, onPaste(e) { e.preventDefault(); const txt = (e.clipboardData?.getData('text') || '').trim(); const chars = Array.from(txt).map(c => this.normalize(c)).filter(c => c); for (let i = 0; i < this.digits.length; i++) { this.digits[i] = chars[i] ?? ''; } let lastFilled = -1; for (let i = 0; i < this.digits.length; i++) { if (this.digits[i]) lastFilled = i; } const target = lastFilled === this.digits.length - 1 ? lastFilled : Math.min(lastFilled + 1, this.digits.length - 1); if (target >= 0) this.$nextTick(() => this.$refs['pin' + target]?.focus()); }, }">
<div class="flex items-center gap-2">
<input type="text" x-model="digits[0]" x-ref="pin0" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="one-time-code" x-on:input="onInput(0, $event)" x-on:keydown="onKeydown(0, $event)" x-on:paste="onPaste($event)" aria-label="Digit 1" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[1]" x-ref="pin1" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(1, $event)" x-on:keydown="onKeydown(1, $event)" aria-label="Digit 2" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[2]" x-ref="pin2" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(2, $event)" x-on:keydown="onKeydown(2, $event)" aria-label="Digit 3" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[3]" x-ref="pin3" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(3, $event)" x-on:keydown="onKeydown(3, $event)" aria-label="Digit 4" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
</div>
</div>
<div class="w-full" x-data="{ digits: Array.from({ length: 4 }, (_, i) => ([])[i] ?? ''), get combined() { return this.digits.join(''); }, normalize(c) { if (!c) return ''; const ch = c.slice(-1); return /[0-9]/.test(ch) ? ch : ''; }, onInput(idx, e) { const ch = this.normalize(e.target.value); this.digits[idx] = ch; if (ch && idx < this.digits.length - 1) { this.$nextTick(() => this.$refs['pin' + (idx + 1)]?.focus()); } }, onKeydown(idx, e) { if (e.key === 'Backspace' && !this.digits[idx] && idx > 0) { this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowLeft' && idx > 0) { e.preventDefault(); this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowRight' && idx < this.digits.length - 1) { e.preventDefault(); this.$refs['pin' + (idx + 1)]?.focus(); } }, onPaste(e) { e.preventDefault(); const txt = (e.clipboardData?.getData('text') || '').trim(); const chars = Array.from(txt).map(c => this.normalize(c)).filter(c => c); for (let i = 0; i < this.digits.length; i++) { this.digits[i] = chars[i] ?? ''; } let lastFilled = -1; for (let i = 0; i < this.digits.length; i++) { if (this.digits[i]) lastFilled = i; } const target = lastFilled === this.digits.length - 1 ? lastFilled : Math.min(lastFilled + 1, this.digits.length - 1); if (target >= 0) this.$nextTick(() => this.$refs['pin' + target]?.focus()); }, }">
<div class="flex items-center gap-2">
<input type="text" x-model="digits[0]" x-ref="pin0" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="one-time-code" x-on:input="onInput(0, $event)" x-on:keydown="onKeydown(0, $event)" x-on:paste="onPaste($event)" aria-label="Digit 1" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-14 h-[var(--h-field-lg)] text-2xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[1]" x-ref="pin1" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(1, $event)" x-on:keydown="onKeydown(1, $event)" aria-label="Digit 2" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-14 h-[var(--h-field-lg)] text-2xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[2]" x-ref="pin2" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(2, $event)" x-on:keydown="onKeydown(2, $event)" aria-label="Digit 3" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-14 h-[var(--h-field-lg)] text-2xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[3]" x-ref="pin3" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(3, $event)" x-on:keydown="onKeydown(3, $event)" aria-label="Digit 4" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-14 h-[var(--h-field-lg)] text-2xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
</div>
</div>
error state — box border が error に flip
Code expired — request a new one
<x-pin-input name="otp_error" label="Verification code" :length="6" error="Code expired — request a new one" />
<div class="w-full" x-data="{ digits: Array.from({ length: 6 }, (_, i) => ([])[i] ?? ''), get combined() { return this.digits.join(''); }, normalize(c) { if (!c) return ''; const ch = c.slice(-1); return /[0-9]/.test(ch) ? ch : ''; }, onInput(idx, e) { const ch = this.normalize(e.target.value); this.digits[idx] = ch; if (ch && idx < this.digits.length - 1) { this.$nextTick(() => this.$refs['pin' + (idx + 1)]?.focus()); } }, onKeydown(idx, e) { if (e.key === 'Backspace' && !this.digits[idx] && idx > 0) { this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowLeft' && idx > 0) { e.preventDefault(); this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowRight' && idx < this.digits.length - 1) { e.preventDefault(); this.$refs['pin' + (idx + 1)]?.focus(); } }, onPaste(e) { e.preventDefault(); const txt = (e.clipboardData?.getData('text') || '').trim(); const chars = Array.from(txt).map(c => this.normalize(c)).filter(c => c); for (let i = 0; i < this.digits.length; i++) { this.digits[i] = chars[i] ?? ''; } let lastFilled = -1; for (let i = 0; i < this.digits.length; i++) { if (this.digits[i]) lastFilled = i; } const target = lastFilled === this.digits.length - 1 ? lastFilled : Math.min(lastFilled + 1, this.digits.length - 1); if (target >= 0) this.$nextTick(() => this.$refs['pin' + target]?.focus()); }, }">
<label class="block mb-1.5 text-[length:var(--text-field-sm)] font-medium text-error">Verification code</label>
<div class="flex items-center gap-2">
<input type="text" x-model="digits[0]" x-ref="pin0" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="one-time-code" x-on:input="onInput(0, $event)" x-on:keydown="onKeydown(0, $event)" x-on:paste="onPaste($event)" aria-label="Digit 1" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-error focus:border-error focus:ring-2 focus:ring-error/30">
<input type="text" x-model="digits[1]" x-ref="pin1" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(1, $event)" x-on:keydown="onKeydown(1, $event)" aria-label="Digit 2" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-error focus:border-error focus:ring-2 focus:ring-error/30">
<input type="text" x-model="digits[2]" x-ref="pin2" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(2, $event)" x-on:keydown="onKeydown(2, $event)" aria-label="Digit 3" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-error focus:border-error focus:ring-2 focus:ring-error/30">
<input type="text" x-model="digits[3]" x-ref="pin3" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(3, $event)" x-on:keydown="onKeydown(3, $event)" aria-label="Digit 4" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-error focus:border-error focus:ring-2 focus:ring-error/30">
<input type="text" x-model="digits[4]" x-ref="pin4" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(4, $event)" x-on:keydown="onKeydown(4, $event)" aria-label="Digit 5" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-error focus:border-error focus:ring-2 focus:ring-error/30">
<input type="text" x-model="digits[5]" x-ref="pin5" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(5, $event)" x-on:keydown="onKeydown(5, $event)" aria-label="Digit 6" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-error focus:border-error focus:ring-2 focus:ring-error/30">
</div><input type="hidden" name="otp_error" x-bind:value="combined">
<p class="mt-1.5 text-[length:var(--text-field-sm)] text-error">
Code expired — request a new one
</p>
</div>
paste 試し — 適当な 6 桁数字をコピーして 1 つ目の box に paste すると全 box が一気に埋まる
Paste the whole code at once — it spreads across the cells
<x-pin-input :length="6" hint="Paste the whole code at once — it spreads across the cells" />
<div class="w-full" x-data="{ digits: Array.from({ length: 6 }, (_, i) => ([])[i] ?? ''), get combined() { return this.digits.join(''); }, normalize(c) { if (!c) return ''; const ch = c.slice(-1); return /[0-9]/.test(ch) ? ch : ''; }, onInput(idx, e) { const ch = this.normalize(e.target.value); this.digits[idx] = ch; if (ch && idx < this.digits.length - 1) { this.$nextTick(() => this.$refs['pin' + (idx + 1)]?.focus()); } }, onKeydown(idx, e) { if (e.key === 'Backspace' && !this.digits[idx] && idx > 0) { this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowLeft' && idx > 0) { e.preventDefault(); this.$refs['pin' + (idx - 1)]?.focus(); } else if (e.key === 'ArrowRight' && idx < this.digits.length - 1) { e.preventDefault(); this.$refs['pin' + (idx + 1)]?.focus(); } }, onPaste(e) { e.preventDefault(); const txt = (e.clipboardData?.getData('text') || '').trim(); const chars = Array.from(txt).map(c => this.normalize(c)).filter(c => c); for (let i = 0; i < this.digits.length; i++) { this.digits[i] = chars[i] ?? ''; } let lastFilled = -1; for (let i = 0; i < this.digits.length; i++) { if (this.digits[i]) lastFilled = i; } const target = lastFilled === this.digits.length - 1 ? lastFilled : Math.min(lastFilled + 1, this.digits.length - 1); if (target >= 0) this.$nextTick(() => this.$refs['pin' + target]?.focus()); }, }">
<div class="flex items-center gap-2">
<input type="text" x-model="digits[0]" x-ref="pin0" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="one-time-code" x-on:input="onInput(0, $event)" x-on:keydown="onKeydown(0, $event)" x-on:paste="onPaste($event)" aria-label="Digit 1" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[1]" x-ref="pin1" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(1, $event)" x-on:keydown="onKeydown(1, $event)" aria-label="Digit 2" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[2]" x-ref="pin2" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(2, $event)" x-on:keydown="onKeydown(2, $event)" aria-label="Digit 3" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[3]" x-ref="pin3" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(3, $event)" x-on:keydown="onKeydown(3, $event)" aria-label="Digit 4" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[4]" x-ref="pin4" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(4, $event)" x-on:keydown="onKeydown(4, $event)" aria-label="Digit 5" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
<input type="text" x-model="digits[5]" x-ref="pin5" pattern="[0-9]" inputmode="numeric" maxlength="1" autocomplete="off" x-on:input="onInput(5, $event)" x-on:keydown="onKeydown(5, $event)" aria-label="Digit 6" class="rounded-[var(--radius-field)] bg-base-100 text-center font-semibold tabular-nums focus:outline-none transition-all disabled:opacity-50 disabled:cursor-not-allowed w-12 h-[var(--h-field-md)] text-xl tune-border border-base-300 focus:border-primary focus:ring-2 focus:ring-primary/30">
</div>
<p class="mt-1.5 text-[length:var(--text-field-sm)] text-base-content/50">
Paste the whole code at once — it spreads across the cells
</p>
</div>