{
 "id": "field-error",
 "name": "Inline field error",
 "purpose": "A form field that says what is wrong right under it and how to fix it, keeps what the person typed, and clears the message once it's right.",
 "use_when": [
  "for a value in a form that can't be accepted as typed",
  "one field's value can be checked and fixed by the person"
 ],
 "avoid_when": [
  "the server refused the whole form: show an error banner above the form (page-banners) and keep every value",
  "a warning, not an error: say it as a hint, not in the error colour"
 ],
 "type": "states",
 "style": "quiet",
 "width": "narrow",
 "tags": {
  "audience": "work",
  "industries": [
   "any"
  ],
  "keywords": [
   "form",
   "validation",
   "error message",
   "input",
   "inline error"
  ]
 },
 "root": ".odl-field-error",
 "parts": [
  {
   "name": "value",
   "selector": ".odl-field-error [data-od-slot=\"value\"]",
   "what": "what the person typed",
   "slot": "text"
  },
  {
   "name": "error",
   "selector": ".odl-field-error [data-od-slot=\"error\"]",
   "what": "what is wrong and how to fix it, as in “Add the part after the @ with a dot, like name@c”",
   "slot": "text"
  },
  {
   "name": "invite",
   "selector": ".odl-field-error [data-od-action=\"invite\"]",
   "what": "the Send invite action",
   "slot": "action"
  }
 ],
 "data": {
  "type": "object",
  "properties": {
   "value": {
    "type": "string",
    "maxLength": 24,
    "part": "value"
   },
   "error": {
    "type": "string",
    "maxLength": 96,
    "part": "error"
   }
  },
  "required": [
   "value",
   "error"
  ]
 },
 "states": {
  "shown": [
   "error",
   "fixed",
   "submitted",
   "hover",
   "focus"
  ],
  "edge": {
   "empty": {
    "status": "shown",
    "how": "An empty field says what to enter, on submit.",
    "evidence": "Enter your work email."
   },
   "loading": {
    "status": "handled",
    "evidence": "data-od-slot",
    "how": "the Smart UI renderer draws each marked slot as a quiet block of its size while the data arrives; the static file shows the filled part"
   },
   "error": {
    "status": "shown",
    "how": "The message sits under the field, with an icon and a thicker danger border.",
    "evidence": "aria-invalid"
   },
   "disabled": {
    "status": "not_built",
    "how": "No control here is ever disabled; each one always does something."
   },
   "overflow": {
    "status": "handled",
    "how": "The message wraps under the field.",
    "evidence": "align-items: start"
   },
   "long_text": {
    "status": "handled",
    "how": "Titles balance and body text wraps at word boundaries inside a capped measure.",
    "evidence": "text-wrap"
   },
   "missing_image": {
    "status": "none",
    "how": "the part has no photo"
   }
  }
 },
 "tokens": [
  "--ds-border-width-default",
  "--ds-border-width-emphasis",
  "--ds-color-bg-action-primary",
  "--ds-color-bg-action-primary-hover",
  "--ds-color-bg-action-primary-pressed",
  "--ds-color-bg-field",
  "--ds-color-bg-neutral-subtle",
  "--ds-color-border-danger",
  "--ds-color-border-default",
  "--ds-color-border-focus",
  "--ds-color-border-input",
  "--ds-color-border-subtle",
  "--ds-color-surface-raised",
  "--ds-color-text-danger",
  "--ds-color-text-link",
  "--ds-color-text-on-action",
  "--ds-color-text-primary",
  "--ds-color-text-success",
  "--ds-color-text-tertiary",
  "--ds-focus-ring-offset",
  "--ds-focus-ring-width",
  "--ds-font-weight-heading",
  "--ds-font-weight-label",
  "--ds-icon-stroke-md",
  "--ds-motion-duration-short",
  "--ds-motion-easing-standard",
  "--ds-opacity-disabled-container",
  "--ds-radius-container",
  "--ds-radius-control",
  "--ds-size-control-md",
  "--ds-size-icon-sm",
  "--ds-size-target-min",
  "--ds-space-inline-sm",
  "--ds-space-inline-xs",
  "--ds-space-inset-lg",
  "--ds-space-inset-md",
  "--ds-space-inset-sm",
  "--ds-space-inset-xl",
  "--ds-space-stack-lg",
  "--ds-space-stack-xs",
  "--ds-state-error-border",
  "--ds-state-error-gap",
  "--ds-state-error-text",
  "--ds-text-body-md",
  "--ds-text-body-sm",
  "--ds-text-label-lg",
  "--ds-text-label-md",
  "--ds-text-title-md"
 ],
 "a11y": {
  "summary": "The label wraps the input; the input is aria-invalid while wrong and described by the message and the hint (the script gives each copy its own ids). The message is a polite live region, checked on leaving the field and on submit, never on every key, and it clears as soon as the value is right; on a failed submit, focus moves to the field.",
  "roles": [
   "form",
   "status"
  ],
  "keyboard": [
   "Tab reaches the email field, then Send invite",
   "On a failed send, focus returns to the field and its message is read"
  ],
  "controls": {
   "button": 1,
   "input": 1,
   "link": 0,
   "select": 0,
   "textarea": 0,
   "summary": 0
  },
  "focus_visible": true,
  "reduced_motion": true,
  "min_target": true
 },
 "files": {
  "html": "components/field-error/component.html",
  "bytes": 7800,
  "script": true
 },
 "source": null,
 "photos": 0,
 "urls": {
  "html": "https://opendesigner.dev/library/field-error.html",
  "json": "https://opendesigner.dev/library/field-error.json",
  "page": "https://opendesigner.dev/library/field-error/"
 },
 "get": {
  "curl": "curl -fsSLo field-error.html https://opendesigner.dev/library/field-error.html",
  "opendesigner": "python3 path/to/skills/opendesigner/scripts/library.py add field-error --from https://opendesigner.dev",
  "prompt": "Add the OpenDesigner component field-error to this project: https://opendesigner.dev/library/field-error.html. Keep its markup and states; map its --ds-* variables to my tokens."
 }
}
