{ "$schema": "../schemas/code-crispies-module-schema.json", "id": "html-forms-fieldset", "title": "Fieldsets", "description": "Group form controls with fieldset and legend elements", "mode": "html", "difficulty": "beginner", "lessons": [ { "id": "fieldset-basic", "title": "Grouping with Fieldset", "description": "The <fieldset> element groups related form controls together. Add a <legend> as the first child to give the group a title.

This helps with accessibility and visual organization of complex forms.", "task": "Create a form with a fieldset:
1. A <form> element
2. A <fieldset> inside
3. A <legend> saying Personal Info
4. Two labeled inputs for name and email", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #f0f4f8; } form { max-width: 400px; } fieldset { border: 2px solid #3498db; border-radius: 10px; padding: 20px; background: white; } legend { color: #3498db; font-weight: 600; padding: 0 10px; font-size: 1.1rem; } label { display: block; margin: 15px 0 5px; color: #333; font-weight: 500; } input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; } input:focus { outline: 2px solid #3498db; border-color: transparent; }", "sandboxCSS": "", "initialCode": "", "solution": "
\n
\n Personal Info\n \n \n \n \n
\n
", "previewContainer": "preview-area", "concept": { "explanation": "Fieldset creates a semantic grouping that browsers and assistive technology understand as related form controls, not just a visual border. When a screen reader enters a fieldset, it announces the legend before each control (\"Personal Info, Name, edit text\"), providing context without repetition in every label. The browser also establishes a form control context: disabling the fieldset (disabled attribute) automatically disables all inputs inside, and form validation can be scoped to fieldsets. This semantic structure is crucial for complex forms—it transforms a flat list of inputs into a hierarchical document with clear relationships, improving both accessibility and maintainability.", "diagram": "Fieldset Semantic Structure\n\nHTML:\n
\n
\n Personal Info\n \n \n
\n
\n\nScreen Reader Experience:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\"Personal Info, group\"\n → Focus first input\n \"Personal Info, Name, edit text\"\n → Tab to next input\n \"Personal Info, Email, edit text\"\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nContext announced once, then reused\n\nDisabled Propagation:\n\n
\n ← Automatically disabled\n ← Automatically disabled\n
\n\nvs Individual Disabling:\n\n ← Must repeat\n\nForm Structure:\n\nFlat (no grouping):\n✗ Name\n✗ Email\n✗ Street\n✗ City\n\nGrouped (semantic):\n✓ Personal Info\n ✓ Name\n ✓ Email\n✓ Address\n ✓ Street\n ✓ City" }, "validations": [ { "type": "element_exists", "value": "form", "message": "Add a <form> element" }, { "type": "element_exists", "value": "fieldset", "message": "Add a <fieldset> inside the form" }, { "type": "element_exists", "value": "legend", "message": "Add a <legend> to title your fieldset" }, { "type": "element_count", "value": { "selector": "label", "min": 2 }, "message": "Add at least 2 labels" }, { "type": "element_count", "value": { "selector": "input", "min": 2 }, "message": "Add at least 2 input fields" } ] }, { "id": "fieldset-textarea", "title": "Adding Textarea", "description": "The <textarea> element creates a multi-line text input, perfect for longer content like messages or descriptions.

Use rows and cols attributes to set default size.", "task": "Create a contact form:
1. A <fieldset> with <legend> Contact Us
2. A labeled <input> for email
3. A labeled <textarea> for the message
4. A submit <button>", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; margin: 0; box-sizing: border-box; } form { max-width: 450px; margin: 0 auto; } fieldset { border: none; border-radius: 15px; padding: 30px; background: white; box-shadow: 0 10px 40px rgba(0,0,0,0.2); } legend { color: #667eea; font-weight: 700; padding: 0; font-size: 1.5rem; margin-bottom: 10px; } label { display: block; margin: 20px 0 8px; color: #333; font-weight: 500; } input, textarea { width: 100%; padding: 12px; border: 2px solid #e0e0e0; border-radius: 8px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: none; border-color: #667eea; } textarea { resize: vertical; min-height: 100px; } button { margin-top: 20px; width: 100%; padding: 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; } button:hover { opacity: 0.9; }", "sandboxCSS": "", "initialCode": "", "solution": "
\n
\n Contact Us\n \n \n \n \n \n
\n
", "previewContainer": "preview-area", "concept": { "explanation": "The textarea element is designed for multi-line text input, automatically providing scroll bars when content exceeds its dimensions and preserving line breaks and whitespace on submission. Unlike input elements which ignore Enter key (using it for form submission), textarea captures Enter as a newline character, making it suitable for addresses, comments, messages, or any free-form text. The rows and cols attributes set initial dimensions as character counts (rows for lines, cols for width in characters), but CSS width/height override these. Textarea is a container element (not self-closing), so you must use <textarea>content</textarea> syntax—any text between the tags becomes the initial value, preserving formatting.", "diagram": "Textarea vs Input\n\nInput (single-line):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\n┌─────────────────────────────┐\n│ Hello_ │\n└─────────────────────────────┘\n✗ Enter → Submits form\n✗ No line breaks\n✗ Self-closing tag\n\nTextarea (multi-line):\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n\n┌─────────────────────────────┐\n│ Hello │\n│ World_ │\n│ │ ← rows=\"3\"\n└─────────────────────────────┘\n✓ Enter → New line\n✓ Preserves line breaks\n✓ Container element\n✓ Auto-scrolls if overflowing\n\nSizing:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nrows=\"4\" → 4 lines tall\ncols=\"40\" → 40 chars wide\nCSS overrides → width/height\n\nValue Syntax:\n\nvs\n" }, "validations": [ { "type": "element_exists", "value": "fieldset", "message": "Add a <fieldset> element" }, { "type": "element_exists", "value": "legend", "message": "Add a <legend> element" }, { "type": "element_exists", "value": "textarea", "message": "Add a <textarea> for the message" }, { "type": "element_exists", "value": "button", "message": "Add a submit button" }, { "type": "element_exists", "value": "input", "message": "Add an input field for email" } ] }, { "id": "fieldset-multiple", "title": "Multiple Fieldsets", "description": "Complex forms can use multiple <fieldset> elements to organize different sections.

This improves usability for long forms like registration or checkout pages.", "task": "Create a registration form with 2 fieldsets:
1. Account Info with username and password inputs
2. Preferences with a textarea for bio
3. A submit button outside the fieldsets", "previewHTML": "", "previewBaseCSS": "body { font-family: system-ui; padding: 20px; background: #fafafa; } form { max-width: 500px; } fieldset { border: 1px solid #ddd; border-radius: 10px; padding: 20px; margin-bottom: 20px; background: white; } legend { color: #2c3e50; font-weight: 600; padding: 0 10px; } label { display: block; margin: 15px 0 5px; color: #555; } input, textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; box-sizing: border-box; font-family: inherit; } input:focus, textarea:focus { outline: 2px solid #3498db; border-color: transparent; } textarea { resize: vertical; min-height: 80px; } button { width: 100%; padding: 14px; background: #2c3e50; color: white; border: none; border-radius: 8px; font-size: 16px; cursor: pointer; } button:hover { background: #34495e; }", "sandboxCSS": "", "initialCode": "", "solution": "
\n
\n Account Info\n \n \n \n \n
\n
\n Preferences\n \n \n
\n \n
", "previewContainer": "preview-area", "concept": { "explanation": "Multiple fieldsets divide long forms into logical sections, improving cognitive load by chunking related fields together—users process \"fill out personal info\" and \"fill out address\" as distinct mental tasks rather than one overwhelming list. This pattern also enables progressive disclosure: you can hide/show fieldsets as wizard steps, disable future sections until current ones validate, or use CSS to style sections differently based on state. Screen readers announce fieldset boundaries (\"entering Personal Info group\", \"leaving Personal Info group\"), helping users maintain their place in complex forms. The semantic structure also aids form analytics: you can track which sections users struggle with or abandon most frequently.", "diagram": "Multi-Fieldset Forms\n\nSingle Long Form:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n┌─────────────────────────────┐\n│ Name: [ ] │\n│ Email: [ ] │\n│ Username: [ ] │ Overwhelming\n│ Password: [ ] │ 8 fields at once\n│ Street: [ ] │\n│ City: [ ] │\n│ State: [ ] │\n│ Bio: [ ] │\n│ [Submit] │\n└─────────────────────────────┘\n\nChunked with Fieldsets:\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n┌─────────────────────────────┐\n│ Personal Info │ Section 1\n│ Name: [ ] │ (2 fields)\n│ Email: [ ] │\n├─────────────────────────────┤\n│ Account │ Section 2\n│ Username: [ ] │ (2 fields)\n│ Password: [ ] │\n├─────────────────────────────┤\n│ Address │ Section 3\n│ Street: [ ] │ (3 fields)\n│ City: [ ] │\n│ State: [ ] │\n├─────────────────────────────┤\n│ About You │ Section 4\n│ Bio: [ ] │ (1 field)\n├─────────────────────────────┤\n│ [Submit] │\n└─────────────────────────────┘\n\nBenefits:\n✓ Reduced cognitive load\n✓ Clear visual hierarchy\n✓ Section-level validation\n✓ Progressive disclosure\n✓ Better analytics" }, "validations": [ { "type": "element_count", "value": { "selector": "fieldset", "min": 2 }, "message": "Create at least 2 fieldsets" }, { "type": "element_count", "value": { "selector": "legend", "min": 2 }, "message": "Add a legend to each fieldset" }, { "type": "element_exists", "value": "textarea", "message": "Add a textarea for the bio" }, { "type": "element_exists", "value": "button", "message": "Add a submit button" }, { "type": "element_count", "value": { "selector": "input", "min": 2 }, "message": "Add at least 2 input fields" } ] } ] }