OTP input
שדה לקודים חד-פעמיים, עם תא לכל תו.
- דפדפנים
- Chrome 123+, Firefox 121+, Safari 17.5+
- שכבה
- 2
- גודל חבילה דחוסה
- 1.67 kB
- מגבלת פריסה כפויה
- 0 ms
- מגבלת Axe
- 0
- תואם SSR
- כן
- חוזה מפורסם
- סביבת בדיקות
מגרש ניסויים
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
import { OtpInput } from "@gunkin/uiify";
export default function Example() {
return (
<OtpInput.Root className="" id="verification-code" length={6}>
{Array.from({ length: 6 }, (_, index) => (
<OtpInput.Slot className="" index={index} key={index} />
))}
<OtpInput.HiddenInput />
</OtpInput.Root>
);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
@layer components {
[data-uiify-otp-input] {
position: relative;
display: inline-flex;
gap: var(--space-sm);
cursor: text;
}
[data-uiify-otp-input] [data-part="slot"] {
display: inline-flex;
align-items: center;
justify-content: center;
inline-size: var(--uiify-control-size, var(--control-md));
block-size: var(--uiify-control-size, var(--control-md));
border: var(--border-width) solid var(--input);
border-radius: var(--radius-full);
background-color: var(--card);
color: var(--foreground);
font-size: var(--uiify-control-font, var(--text-base));
font-weight: var(--weight-medium);
transition: var(--transition-colors);
}
[data-uiify-otp-input] [data-part="slot"][data-active] {
border-color: var(--primary);
outline: 2px solid color-mix(in oklch, var(--ring) 35%, transparent);
outline-offset: 0;
}
[data-uiify-otp-input] [data-part="slot"][data-active]::after {
inline-size: 1px;
block-size: 1em;
background-color: currentcolor;
content: "";
animation: uiify-otp-input-caret-blink 1s step-end infinite;
}
@keyframes uiify-otp-input-caret-blink {
50% {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
[data-uiify-otp-input] [data-part="slot"][data-active]::after {
animation: none;
}
}
[data-uiify-otp-input] [data-part="slot"][data-filled] {
border-color: var(--input);
}
[data-uiify-otp-input][data-disabled] [data-part="slot"] {
opacity: 0.5;
cursor: not-allowed;
}
}
@layer components {
[data-uiify-otp-input]:not([data-disabled]) {
cursor: text;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
import { OtpInput } from "@gunkin/uiify";
export default function Example() {
return (
<OtpInput.Root className="relative inline-flex gap-sm [cursor:text]" id="verification-code" length={6}>
{Array.from({ length: 6 }, (_, index) => (
<OtpInput.Slot
className="
grid place-items-center [inline-size:var(--control-md)] [block-size:var(--control-md)]
border-[length:var(--border-width)] border-solid border-input rounded-full bg-card text-foreground text-base
font-medium tabular-nums [transition:var(--transition-colors)] data-[active]:border-primary
data-[active]:[outline:2px_solid_color-mix(in_oklch,_var(--ring)_35%,_transparent)]
data-[active]:[outline-offset:0] data-[active]:after:content-[''] data-[active]:after:[inline-size:1px]
data-[active]:after:[block-size:1em] data-[active]:after:bg-current
data-[active]:after:[animation:uiify-otp-input-caret-blink_1s_step-end_infinite]
motion-reduce:data-[active]:after:[animation:none] data-[filled]:border-input data-[disabled]:opacity-50
data-[disabled]:cursor-not-allowed
"
index={index}
key={index}
/>
))}
<OtpInput.HiddenInput />
</OtpInput.Root>
);
}
API
OtpInput.Root
size | "sm" | "md" | "lg" | "md" | גובה הפקד וגודל הטקסט, בהתאמה ל-Button (32, 40 או 48 px); נכתבים ל-data-size, עם md כשלא הוגדר ערך. |
value | string | — | הקוד הנוכחי, ההופך את השדה למבוקר. |
defaultValue | string | "" | הקוד ההתחלתי של השדה הלא מבוקר. |
onValueChange | (value: string) => void | — | נקראת עם הקוד בכל שינוי בו. |
onComplete | (value: string) => void | — | נקראת כשהקוד מגיע לאורך שהוגדר. |
length | number | 6 | מספר התווים שהקוד מכיל. |
type | "numeric" | "alphanumeric" | "numeric" | התווים שנשמרים בעת הקלדה או הדבקה. |
mask | boolean | false | מסתיר את התווים שהוזנו במשבצות. |
disabled | boolean | false | משבית את הקלט המוסתר ומגדיר את המאפיין data-disabled. |
autoFocus | boolean | false | ממקד את הקלט המוסתר כשהשדה נטען. |
id | string | — | המזהה המוחל על אלמנט השורש המוצג. |
render | ReactElement | ((props, state) => ReactElement) | — | החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות. |
OtpInput.Slot
index* | number | — | המיקום בקוד שמשבצת זו מציגה. |
render | ReactElement | ((props, state) => ReactElement) | — | החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות. |
OtpInput.HiddenInput
autoFocus | boolean | — | ממקד קלט זה בעת הטעינה, תוך עקיפת הגדרת השורש. |
render | ReactElement | ((props, state) => ReactElement) | — | החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות. |
מקלדת
| מקש | פעולה |
|---|
| Tab | העברת המיקוד אל הפקד או ממנו |
| Enter / Space | הפעלה כשהפקד ממוקד (פקדים דמויי כפתור) |
נגישות
משתמש ב-HTML סמנטי ככל האפשר. בדקו את הסימון המרונדר במגרש הניסויים — ARIA נוסף רק כאשר האלמנטים המובנים אינם מספיקים.
נקודות חיבור לעיצוב
data-disabled