{"id":"e8b78ede-12f6-4cfa-ad2e-14dd859dc526","shortId":"jQSDCL","kind":"skill","title":"form-design","tagline":"Forms have three layers of guidance: helper text below the input explains what to enter, placeholder shows the expected format, and validation confirms correctness. Real-time validation for complex inputs. Submit enables only when the form is valid. Use when designing or reviewin","description":"# Form Design\n\nForms are where users give the product data. Every unnecessary obstacle between the user and a completed form is a failure. The design goal is to make correct input easy and incorrect input obvious — before the user submits.\n\n---\n\n## The Three Guidance Layers\n\nEach layer serves a distinct purpose. Do not collapse them.\n\n### Layer 1 — Helper Text\nExplains *what* to enter. Appears below the input, always visible, in small secondary text.\n\n```\nEmail address\n[                              ]\nUse the email you signed up with.\n```\n\n- Write in plain language from the user's perspective\n- Keep it to one sentence — if you need more, the field is too complex or misnamed\n- Do not repeat the label (\"Enter your email\" below a label that says \"Email\" is redundant)\n- Helper text is not a replacement for a label — the label is still required\n\n### Layer 2 — Placeholder\nShows the *format* or an example value. Appears inside the input, disappears on typing.\n\n```\n[jane@example.com              ]\n```\n\n- Use a realistic example, not a description: `+358 40 123 4567` not `Enter phone number`\n- Never use placeholder as a label — it disappears and leaves the user without context\n- Keep it grey (`--color-text-secondary`) and lighter than actual input text\n- Optional — not every field needs a placeholder\n\n### Layer 3 — Validation\nConfirms whether the input is correct. The most important layer.\n\n```\nEmail address\n[jane@           ] ← invalid\n✗ Enter a valid email address.\n```\n\n**Validation timing:**\n- **On blur** (leaving the field): default for most fields — validates once the user has finished\n- **Real-time** (on input): use when the format is complex or the error is likely — password strength, IBAN, VAT number, URL, regex-heavy fields\n- **On submit**: catches anything missed, scrolls to the first error\n\nReal-time validation must be forgiving at the start — do not show an error the instant the user starts typing. Show it after a short debounce (300–500ms) or after the first character that makes the input definitively wrong.\n\n---\n\n## Submit Button State\n\nThe submit button enables when the form is valid. This is one of the clearest affordance signals in form design — the user sees the goal and knows when they have reached it.\n\n```\n[Submit]   ← disabled, low contrast, cursor: not-allowed\n           (fields incomplete or invalid)\n\n[Submit]   ← enabled, full colour, cursor: pointer\n           (all required fields valid)\n```\n\n**Implementation:**\n```html\n<button type=\"submit\" disabled={!isFormValid}>Submit</button>\n```\n\nFor long or complex forms where real-time validation is not practical, do not disable the submit — validate on submit and scroll to errors instead. Disabled submit on a long form frustrates users who cannot tell what is missing.\n\n**Loading state on submit:** Replace label with spinner, disable the button. Prevent double-submission.\n\n---\n\n## Field Anatomy\n\n```\n[Label]                           [Optional badge if optional]\n[Input field                                                  ]\n[Helper text — what to enter, format, constraints            ]\n[Error message — appears below helper text on validation fail ]\n```\n\n```html\n<div class=\"field\">\n  <label for=\"vat\">VAT number <span class=\"optional\">Optional</span></label>\n  <input\n    id=\"vat\"\n    type=\"text\"\n    placeholder=\"FI12345678\"\n    aria-describedby=\"vat-helper vat-error\"\n    aria-invalid=\"true\"\n  >\n  <p id=\"vat-helper\" class=\"helper-text\">Finnish VAT numbers start with FI followed by 8 digits.</p>\n  <p id=\"vat-error\" class=\"error-text\" role=\"alert\">Enter a valid Finnish VAT number (e.g. FI12345678).</p>\n</div>\n```\n\n---\n\n## Required vs Optional\n\nMark the minority. If most fields are required, mark the optional ones. If most are optional, mark the required ones.\n\n- Do not rely on colour alone — add a text label (\"Required\" or asterisk with legend)\n- Place the required/optional indicator in the label, not only in the placeholder or helper text\n\n```html\n<label>Email <abbr title=\"Required\">*</abbr></label>\n<!-- or -->\n<label>Phone <span class=\"badge\">Optional</span></label>\n```\n\n---\n\n## Grouping with Fieldset\n\nRelated fields belong in a `<fieldset>` with a `<legend>`. This is semantic HTML and helps screen readers announce the group context.\n\n```html\n<fieldset>\n  <legend>Billing address</legend>\n  <label>Street</label><input type=\"text\">\n  <label>City</label><input type=\"text\">\n  <label>Postal code</label><input type=\"text\">\n</fieldset>\n```\n\nUse fieldsets for:\n- Address groups\n- Payment details\n- Radio button groups\n- Checkbox groups\n\n---\n\n## Input Types\n\nUse the correct `type` — browsers provide free validation, appropriate keyboards, and autofill.\n\n| Data | Input type |\n|---|---|\n| Email | `type=\"email\"` |\n| Phone | `type=\"tel\"` |\n| URL | `type=\"url\"` |\n| Number | `type=\"number\"` |\n| Password | `type=\"password\"` |\n| Date | `type=\"date\"` |\n| Search | `type=\"search\"` |\n| Colour | `type=\"color\"` |\n\nOn mobile, `type=\"email\"` shows the email keyboard, `type=\"tel\"` shows the numpad. These are free UX improvements.\n\n---\n\n## Autofill Support\n\nAllow browsers to autofill. Do not disable it unless there is a security requirement.\n\n```html\n<input type=\"text\"  autocomplete=\"name\">\n<input type=\"email\" autocomplete=\"email\">\n<input type=\"tel\"   autocomplete=\"tel\">\n<input type=\"text\"  autocomplete=\"street-address\">\n<input type=\"text\"  autocomplete=\"postal-code\">\n<input type=\"text\"  autocomplete=\"cc-number\">    <!-- credit card -->\n<input type=\"password\" autocomplete=\"new-password\">\n```\n\nCorrect `autocomplete` values reduce friction dramatically for returning users and on mobile.\n\n---\n\n## Review Checklist\n\n- [ ] Every field has a visible label (not just placeholder)\n- [ ] Helper text is below the input and explains what to enter\n- [ ] Placeholder shows format or example, not a description\n- [ ] Validation triggers on blur for simple fields, real-time for complex ones\n- [ ] Error message is adjacent to the field that failed\n- [ ] Error message is associated via `aria-describedby`\n- [ ] Required/optional marked on the minority of fields\n- [ ] Submit button is disabled when form is invalid (for short forms)\n- [ ] Submit button shows a loading state and prevents re-submission\n- [ ] Related fields are grouped in `<fieldset>` with `<legend>`\n- [ ] Correct `type` attribute on all inputs\n- [ ] `autocomplete` attributes set on address, contact, and payment fields","tags":["form","design","dembrandt","skills","accessibility","agent-skills","claude-code-skills","claude-skills","cursor-skills","design-system","design-tokens","enterprise-ux"],"capabilities":["skill","source-dembrandt","skill-form-design","topic-accessibility","topic-agent-skills","topic-claude-code-skills","topic-claude-skills","topic-cursor-skills","topic-design-system","topic-design-tokens","topic-enterprise-ux","topic-gestalt","topic-skills-sh","topic-typography","topic-ui-design"],"categories":["dembrandt-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dembrandt/dembrandt-skills/form-design","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dembrandt/dembrandt-skills","source_repo":"https://github.com/dembrandt/dembrandt-skills","install_from":"skills.sh"}},"qualityScore":"0.454","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 9 github stars · SKILL.md body (6,405 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:08:26.909Z","embedding":null,"createdAt":"2026-05-18T13:14:01.478Z","updatedAt":"2026-05-18T19:08:26.909Z","lastSeenAt":"2026-05-18T19:08:26.909Z","tsv":"'+358':209 '1':103 '123':211 '2':185 '3':252 '300':353 '40':210 '4567':212 '500ms':354 '8':523 'actual':241 'add':562 'address':121,265,272,614,622,824 'adjac':765 'afford':384 'allow':408,692 'alon':561 'alway':114 'anatomi':487 'announc':608 'anyth':319 'appear':110,194,504 'appropri':641 'aria':777 'aria-describedbi':776 'associ':774 'asterisk':568 'attribut':816,821 'autocomplet':708,820 'autofil':644,690,695 'badg':490 'belong':595 'bill':613 'blur':276,752 'browser':637,693 'button':367,371,425,481,627,787,798 'cannot':466 'catch':318 'charact':359 'checkbox':629 'checklist':720 'citi':616 'clearest':383 'code':618 'collaps':100 'color':235,671 'color-text-secondari':234 'colour':416,560,669 'complet':66 'complex':33,151,300,434,760 'confirm':26,254 'constraint':501 'contact':825 'context':230,611 'contrast':404 'correct':27,77,259,635,707,814 'cursor':405,417 'data':57,645 'date':663,665 'debounc':352 'default':280 'definit':364 'describedbi':778 'descript':208,748 'design':3,45,49,72,388 'detail':625 'digit':524 'disabl':402,428,446,457,479,698,789 'disappear':198,224 'distinct':96 'doubl':484 'double-submiss':483 'dramat':712 'e.g':531 'easi':79 'email':120,124,161,167,264,271,587,648,650,675,678 'enabl':36,372,414 'enter':18,109,159,214,268,499,525,740 'error':303,325,340,455,502,762,771 'everi':58,246,721 'exampl':192,205,745 'expect':22 'explain':15,106,737 'fail':510,770 'failur':70 'fi':520 'fi12345678':532 'field':148,247,279,283,315,409,421,486,494,541,594,722,755,768,785,809,828 'fieldset':592,620 'finish':289 'finnish':515,528 'first':324,358 'follow':521 'forgiv':332 'form':2,4,40,48,50,67,375,387,435,462,791,796 'form-design':1 'format':23,189,298,500,743 'free':639,687 'friction':711 'frustrat':463 'full':415 'give':54 'goal':73,393 'grey':233 'group':590,610,623,628,630,811 'guidanc':9,90 'heavi':314 'help':605 'helper':10,104,170,495,506,584,730 'html':424,511,586,603,612,706 'iban':308 'implement':423 'import':262 'improv':689 'incomplet':410 'incorrect':81 'indic':574 'input':14,34,78,82,113,197,242,257,294,363,493,631,646,735,819 'insid':195 'instant':342 'instead':456 'invalid':267,412,793 'isformvalid':429 'jane':266 'jane@example.com':201 'keep':138,231 'keyboard':642,679 'know':395 'label':158,164,178,180,222,476,488,565,577,726 'languag':132 'layer':7,91,93,102,184,251,263 'leav':226,277 'legend':570 'lighter':239 'like':305 'load':471,801 'long':432,461 'low':403 'make':76,361 'mark':536,544,552,780 'messag':503,763,772 'minor':538,783 'misnam':153 'miss':320,470 'mobil':673,718 'must':330 'need':145,248 'never':217 'not-allow':406 'number':216,310,513,517,530,657,659 'numpad':684 'obstacl':60 'obvious':83 'one':141,380,547,555,761 'option':244,489,492,514,535,546,551,589 'password':306,660,662 'payment':624,827 'perspect':137 'phone':215,588,651 'place':571 'placehold':19,186,219,250,582,729,741 'plain':131 'pointer':418 'postal':617 'practic':443 'prevent':482,804 'product':56 'provid':638 'purpos':97 'radio':626 're':806 're-submiss':805 'reach':399 'reader':607 'real':29,291,327,438,757 'real-tim':28,290,326,437,756 'realist':204 'reduc':710 'redund':169 'regex':313 'regex-heavi':312 'relat':593,808 'reli':558 'repeat':156 'replac':175,475 'requir':183,420,533,543,554,566,705 'required/optional':573,779 'return':714 'review':719 'reviewin':47 'say':166 'screen':606 'scroll':321,453 'search':666,668 'secondari':118,237 'secur':704 'see':391 'semant':602 'sentenc':142 'serv':94 'set':822 'short':351,795 'show':20,187,338,347,676,682,742,799 'sign':126 'signal':385 'simpl':754 'skill' 'skill-form-design' 'small':117 'source-dembrandt' 'spinner':478 'start':335,345,518 'state':368,472,802 'still':182 'street':615 'strength':307 'submiss':485,807 'submit':35,87,317,366,370,401,413,427,430,448,451,458,474,786,797 'support':691 'tel':653,681 'tell':467 'text':11,105,119,171,236,243,496,507,564,585,731 'three':6,89 'time':30,274,292,328,439,758 'topic-accessibility' 'topic-agent-skills' 'topic-claude-code-skills' 'topic-claude-skills' 'topic-cursor-skills' 'topic-design-system' 'topic-design-tokens' 'topic-enterprise-ux' 'topic-gestalt' 'topic-skills-sh' 'topic-typography' 'topic-ui-design' 'trigger':750 'type':200,346,426,632,636,647,649,652,655,658,661,664,667,670,674,680,815 'unless':700 'unnecessari':59 'url':311,654,656 'use':43,122,202,218,295,619,633 'user':53,63,86,135,228,287,344,390,464,715 'ux':688 'valid':25,31,42,253,270,273,284,329,377,422,440,449,509,527,640,749 'valu':193,709 'vat':309,512,516,529 'via':775 'visibl':115,725 'vs':534 'whether':255 'without':229 'write':129 'wrong':365","prices":[{"id":"04d4a7fe-1272-495b-bdaf-f144a3aa51e5","listingId":"e8b78ede-12f6-4cfa-ad2e-14dd859dc526","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dembrandt","category":"dembrandt-skills","install_from":"skills.sh"},"createdAt":"2026-05-18T13:14:01.478Z"}],"sources":[{"listingId":"e8b78ede-12f6-4cfa-ad2e-14dd859dc526","source":"github","sourceId":"dembrandt/dembrandt-skills/form-design","sourceUrl":"https://github.com/dembrandt/dembrandt-skills/tree/main/skills/form-design","isPrimary":false,"firstSeenAt":"2026-05-18T13:14:01.478Z","lastSeenAt":"2026-05-18T19:08:26.909Z"}],"details":{"listingId":"e8b78ede-12f6-4cfa-ad2e-14dd859dc526","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dembrandt","slug":"form-design","github":{"repo":"dembrandt/dembrandt-skills","stars":9,"topics":["accessibility","agent-skills","claude-code-skills","claude-skills","cursor-skills","design-system","design-tokens","enterprise-ux","gestalt","skills-sh","typography","ui-design","ux","wcag"],"license":"mit","html_url":"https://github.com/dembrandt/dembrandt-skills","pushed_at":"2026-05-14T22:34:06Z","description":"UX and design system skills for AI agents based on 20 years of experience","skill_md_sha":"d42518fb030dd39f095fcc714082e787e4b5b631","skill_md_path":"skills/form-design/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dembrandt/dembrandt-skills/tree/main/skills/form-design"},"layout":"multi","source":"github","category":"dembrandt-skills","frontmatter":{"name":"form-design","description":"Forms have three layers of guidance: helper text below the input explains what to enter, placeholder shows the expected format, and validation confirms correctness. Real-time validation for complex inputs. Submit enables only when the form is valid. Use when designing or reviewing any form, input field, or data entry UI."},"skills_sh_url":"https://skills.sh/dembrandt/dembrandt-skills/form-design"},"updatedAt":"2026-05-18T19:08:26.909Z"}}