유효성 검사와 오류 상태
테두리, 링, 안내 텍스트에 조건부 색상 클래스를 사용해 폼 필드의 성공 및 오류 상태를 표시합니다.
유효성 검사와 오류 상태은(는) CoddyKit의 무료 Tailwind CSS Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Tailwind CSS Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Tailwind CSS Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Why Visible Validation Matters
Poor form validation feedback is one of the top causes of form abandonment. Users who fill in a field incorrectly need to know immediately which field is wrong and why. Vague error messages like Form contains errors are not helpful.
With Tailwind, you communicate validation state through three visual channels: the input border color, a colored ring on focus, and an error message beneath the field with an icon. This multi-channel approach helps all users, including those with color vision deficiencies.
Error State Input Styling
An error input switches its border to red: replace border-gray-300 with border-red-500. On focus, show a red ring instead of the usual blue: focus:ring-red-500.
The combination of a red border in the default state and a red ring on focus ensures the error is visible both when the field is active and when the user has moved on to another field. Add a subtle red background tint bg-red-50 for extra emphasis on critical errors.
<!-- Error state input -->
<input
type="email"
value="not-an-email"
aria-invalid="true"
aria-describedby="email-error"
class="w-full px-3 py-2 text-sm text-gray-900 bg-red-50
border border-red-500 rounded-lg
focus:outline-none focus:ring-2 focus:ring-red-500 focus:border-transparent"
/>Error Message Below the Field
An error message appears below the input to explain what went wrong. Use flex items-center gap-1 mt-1 text-xs text-red-600 on the message container. Add an icon for extra clarity — a small exclamation circle in red reinforces the error state visually.
Connect the error message to the input with aria-describedby on the input and a matching id on the message paragraph. This allows screen readers to announce the error automatically when the field receives focus.
<div class="flex flex-col gap-1">
<label for="email" class="text-sm font-medium text-gray-700">Email address</label>
<input
id="email"
type="email"
value="bad-email"
aria-invalid="true"
aria-describedby="email-err"
class="w-full px-3 py-2 text-sm border border-red-500 bg-red-50 rounded-lg
focus:outline-none focus:ring-2 focus:ring-red-500"
/>
<p id="email-err" role="alert" class="flex items-center gap-1 mt-1 text-xs text-red-600">
<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
Please enter a valid email address.
</p>
</div>Success State Input Styling
When a field passes validation, show a success state with a green border and a checkmark icon below the field. Replace border-red-500 with border-green-500 and use focus:ring-green-500 for the focus ring.
A success message uses flex items-center gap-1 mt-1 text-xs text-green-600 with a checkmark icon. Success states are particularly valuable for real-time validation (such as username availability checks) where user confidence benefits from immediate positive feedback.
<div class="flex flex-col gap-1">
<label for="username" class="text-sm font-medium text-gray-700">Username</label>
<input
id="username"
type="text"
value="awesome_dev"
class="w-full px-3 py-2 text-sm border border-green-500 bg-green-50 rounded-lg
focus:outline-none focus:ring-2 focus:ring-green-500"
/>
<p class="flex items-center gap-1 mt-1 text-xs text-green-600">
<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
Username is available!
</p>
</div>Conditional Classes With JavaScript
In real applications, error and success states are applied conditionally based on validation results. In vanilla JavaScript, this means toggling classes on the input and showing/hiding the error message element.
The pattern is to apply a default neutral class set, an error class set, and a success class set — then add exactly one of these to the input when needed. Removing all state classes before adding the new state prevents class conflicts.
<script>
function validateEmail(input) {
const errorEl = document.getElementById('email-error');
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Remove previous states
input.classList.remove(
'border-gray-300', 'border-red-500', 'border-green-500',
'focus:ring-blue-500', 'focus:ring-red-500', 'focus:ring-green-500'
);
if (!emailRegex.test(input.value)) {
input.classList.add('border-red-500', 'focus:ring-red-500');
errorEl.classList.remove('hidden');
} else {
input.classList.add('border-green-500', 'focus:ring-green-500');
errorEl.classList.add('hidden');
}
}
</script>
<input type="email" onblur="validateEmail(this)"
class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />Form-Level Error Summary
For complex forms, display a form-level error summary at the top when the user attempts to submit with multiple invalid fields. This saves users from having to scroll through the entire form to discover all errors.
Style the summary as a red alert box: border border-red-200 bg-red-50 rounded-lg p-4 with an error icon and an unordered list of error messages. Focus this element on display so keyboard and screen reader users immediately hear the summary.
<div role="alert" class="border border-red-200 bg-red-50 rounded-lg p-4 mb-6">
<div class="flex items-center gap-2 mb-2">
<svg class="w-5 h-5 text-red-500" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
<p class="text-sm font-semibold text-red-700">Please fix the following errors:</p>
</div>
<ul class="ml-5 list-disc space-y-1">
<li class="text-sm text-red-600">Email address is required.</li>
<li class="text-sm text-red-600">Password must be at least 8 characters.</li>
</ul>
</div>Warning State for Non-Blocking Issues
A warning state indicates a potentially problematic but non-blocking condition — for example, a password that meets minimum requirements but is weak, or a username that is available but similar to an existing one.
Use amber/yellow for warnings: border-yellow-400 on the input and text-yellow-700 on the message with a warning icon. The warning should not block form submission — it is informational, not a blocking error.
<div class="flex flex-col gap-1">
<label for="pw-warn" class="text-sm font-medium text-gray-700">Password</label>
<input
id="pw-warn"
type="password"
value="password123"
class="w-full px-3 py-2 text-sm border border-yellow-400 rounded-lg
focus:outline-none focus:ring-2 focus:ring-yellow-400"
/>
<p class="flex items-center gap-1 mt-1 text-xs text-yellow-700">
<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
Weak password. Consider adding symbols or uppercase letters.
</p>
</div>Password Strength Indicator
A password strength bar is a visual component that updates in real time as the user types. Implement it as a narrow bar below the input split into segments that fill with color as the password grows stronger.
Use a flex gap-1 container with multiple h-1 rounded-full segments. Color them conditionally: gray for empty, red for weak, yellow for medium, and green for strong. This is a practical example of conditional class application in Tailwind.
<!-- 4-segment strength bar -->
<div class="flex gap-1 mt-2">
<!-- Segment 1: always filled if any input -->
<div class="flex-1 h-1.5 rounded-full bg-red-500"></div>
<!-- Segment 2: filled if medium+ -->
<div class="flex-1 h-1.5 rounded-full bg-yellow-400"></div>
<!-- Segment 3: filled if strong -->
<div class="flex-1 h-1.5 rounded-full bg-gray-200"></div>
<!-- Segment 4: filled if very strong -->
<div class="flex-1 h-1.5 rounded-full bg-gray-200"></div>
</div>
<p class="mt-1 text-xs text-yellow-600 font-medium">Medium strength</p>Shake Animation on Invalid Submit
Adding a brief shake animation to an invalid form on submit provides immediate kinetic feedback. Define a custom shake keyframe in tailwind.config.js and apply it with animate-shake.
The shake animation consists of rapid horizontal translations: 0% → -10px → 10px → -5px → 5px → 0. Trigger it by adding the class on submission and removing it after the animation completes using the animationend event listener.
// tailwind.config.js
module.exports = {
theme: {
extend: {
keyframes: {
shake: {
'0%, 100%': { transform: 'translateX(0)' },
'10%, 30%, 50%, 70%, 90%': { transform: 'translateX(-6px)' },
'20%, 40%, 60%, 80%': { transform: 'translateX(6px)' },
},
},
animation: {
shake: 'shake 0.4s ease-in-out',
},
},
},
};
// Usage:
// form.classList.add('animate-shake');
// form.addEventListener('animationend', () => form.classList.remove('animate-shake'), { once: true });Real-Time Validation Timing
Triggering validation at the right moment is as important as the visual style. Validating on every keystroke (oninput) is annoying for users who have not finished typing. Validating only on submit means users do not see feedback until they are done with the whole form.
The best practice is validate on blur (onblur): show errors when the user leaves a field. Once the error is showing, switch to oninput for that field so the error clears as soon as it is fixed, giving immediate positive feedback.
<script>
let hasError = false;
const input = document.getElementById('live-email');
const errorEl = document.getElementById('live-error');
// Validate on blur (leaving the field)
input.addEventListener('blur', () => {
if (!input.value.includes('@')) {
hasError = true;
input.classList.add('border-red-500');
errorEl.classList.remove('hidden');
}
});
// Once error shown, clear in real time
input.addEventListener('input', () => {
if (hasError && input.value.includes('@')) {
hasError = false;
input.classList.remove('border-red-500');
input.classList.add('border-green-500');
errorEl.classList.add('hidden');
}
});
</script>Accessible Error Announcements
Screen readers do not automatically announce dynamically injected error messages unless the element has role='alert' or aria-live='polite'. Adding role='alert' to the error message paragraph causes the screen reader to interrupt and read the message as soon as it appears in the DOM.
Use aria-invalid='true' on invalid inputs and aria-describedby pointing to the error message ID so screen readers announce the context when the field receives focus.
<div class="flex flex-col gap-1">
<label for="acc-email" class="text-sm font-medium text-gray-700">Email</label>
<input
id="acc-email"
type="email"
aria-invalid="true"
aria-describedby="acc-email-error"
class="w-full px-3 py-2 text-sm border border-red-500 rounded-lg
focus:outline-none focus:ring-2 focus:ring-red-500"
/>
<!-- role=alert makes screen readers announce this when it appears -->
<p id="acc-email-error" role="alert" class="flex items-center gap-1 mt-1 text-xs text-red-600">
<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
Please enter a valid email address.
</p>
</div>Quick Check
Test your understanding of Tailwind CSS Mastery concepts from this lesson.
Lesson Recap
In this lesson you learned: error state inputs use border-red-500 with focus:ring-red-500 and a bg-red-50 tint; error messages appear below the field with text-xs text-red-600 and a warning icon; and accessibility requires aria-invalid, aria-describedby, and role='alert' so screen readers announce errors automatically. Next up we build modal dialog components.
AI 튜터와 함께 HTML을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 30
- 레슨
- 120
자주 묻는 질문
“유효성 검사와 오류 상태” 강의는 무료인가요?
네 — “유효성 검사와 오류 상태” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Tailwind CSS Academy 강의 전체를 잠금 해제할 수 있습니다. Tailwind CSS Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“유효성 검사와 오류 상태”에서 뭘 배우나요?
테두리, 링, 안내 텍스트에 조건부 색상 클래스를 사용해 폼 필드의 성공 및 오류 상태를 표시합니다. 브라우저에서 직접 실행하는 실습 코드로 Tailwind CSS Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Tailwind CSS Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Tailwind CSS Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“유효성 검사와 오류 상태” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Tailwind CSS Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Tailwind CSS Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 텍스트 입력과 텍스트 영역 스타일링
- 선택 메뉴와 체크박스
- 폼 레이아웃 패턴
- 유효성 검사와 오류 상태