{ "$schema": "../schemas/code-crispies-module-schema.json", "id": "html-forms-validation", "title": "HTML Validation", "description": "Learn HTML5 built-in form validation attributes", "mode": "html", "difficulty": "intermediate", "lessons": [ { "id": "required-fields", "title": "Required Fields", "description": "The required attribute prevents form submission if the field is empty.

Add it to any input that must be filled:
<input type=\"text\" required>

The browser shows a validation message automatically.", "task": "Make both the name and email fields required by adding the required attribute.", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 350px; } label { display: block; margin-top: 15px; margin-bottom: 5px; } label:first-of-type { margin-top: 0; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } input:invalid { border-color: #d32f2f; } button { margin-top: 20px; padding: 10px 20px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; }", "sandboxCSS": "", "initialCode": "
\n \n \n \n \n \n \n \n
", "solution": "
\n \n \n \n \n \n \n \n
", "previewContainer": "preview-area", "concept": { "explanation": "The required attribute activates the browser's Constraint Validation API, which checks field values before allowing form submission. When a user tries to submit with empty required fields, the browser automatically focuses the first invalid field, displays a localized error message (\"Please fill out this field\" in English), and blocks the submit event—no JavaScript needed. The :invalid CSS pseudo-class lets you style invalid fields (like red borders), and screen readers announce required fields as \"Email, required, edit text\" so all users know which fields are mandatory.", "diagram": "Native Validation Flow\n\nBefore Submit:\n┌────────────────────────────┐\n│ │ ← Browser monitors\n│ [empty] │ validity state\n└────────────────────────────┘\n :invalid pseudo-class\n\nOn Submit Click:\n┌────────────────────────────┐\n│ Browser checks: │\n│ ✓ Is field filled? │\n│ ✗ Empty → INVALID │\n└────────────┬───────────────┘\n │\n ↓ Blocks submit\n┌────────────┴───────────────┐\n│ [!] Please fill out this │ ← Localized\n│ field │ browser message\n└────────────────────────────┘\n Focus moved here\n\nCSS States Available:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n:valid → Green border\n:invalid → Red border\n:required → Asterisk icon\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" }, "validations": [ { "type": "attribute_value", "value": { "selector": "input[name='name']", "attr": "required", "value": true }, "message": "Add the required attribute to the name input" }, { "type": "attribute_value", "value": { "selector": "input[name='email']", "attr": "required", "value": true }, "message": "Add the required attribute to the email input" } ] }, { "id": "input-constraints", "title": "Constraints", "description": "Control what users can enter:

minlength / maxlength - Text length limits
min / max - Number range
pattern - Regex pattern matching
placeholder - Hint text (not a label!)", "task": "Add validation to the password input:
1. Add minlength=\"8\" for minimum length
2. Add maxlength=\"20\" for maximum length
3. Add placeholder=\"Enter password\" as a hint", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; } form { max-width: 350px; } label { display: block; margin-bottom: 5px; } input { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; } input:invalid:not(:placeholder-shown) { border-color: #d32f2f; } small { display: block; font-size: 12px; color: #666; margin-top: 4px; } button { margin-top: 20px; padding: 10px 20px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; }", "sandboxCSS": "", "initialCode": "
\n \n \n Must be 8-20 characters\n \n \n
", "solution": "
\n \n \n Must be 8-20 characters\n \n \n
", "previewContainer": "preview-area", "concept": { "explanation": "Constraint attributes define validation rules that the browser enforces automatically. The minlength attribute triggers :invalid state and blocks submission if the value is shorter than 8 characters, while maxlength physically prevents typing beyond 20 characters (a hard limit, not just validation). The pattern attribute accepts regex for complex rules like \"uppercase + lowercase + number\" without any JavaScript validation code. Placeholder text disappears when typing starts, so never use it instead of a label—use aria-describedby to link visible hint text for screen reader users.", "diagram": "Constraint Validation Rules\n\nAttribute Behaviors:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nminlength=\"8\" Validates on submit\n Can type less, can't submit\n Error: \"Too short (min 8)\"\n\nmaxlength=\"20\" Prevents typing\n Keyboard blocked at char 20\n No error (can't violate)\n\npattern=\"...\" Regex validation\n Example: \"[A-Z][a-z]+\"\n Error: \"Match format\"\n\nmin=\"1\" max=\"5\" Number range\n For type=\"number\"\n Error: \"Out of range\"\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nAccessibility Pattern:\n┌────────────────────────────┐\n│