{
 "id": "form-fields",
 "name": "Form fields",
 "purpose": "A short form of one to eight fields (text, number, choice, date or switch) with hints, checks as you leave a field, and Continue and Cancel buttons.",
 "use_when": [
  "when the assistant needs a few details it cannot guess, such as a delivery change, a request or a problem report",
  "two to eight details the person must give before the assistant can act, each a short text, a number, a choice from a list, a date or a yes or no"
 ],
 "avoid_when": [
  "one choice from a few answers: use quick-replies",
  "picking an appointment time: use time-slots",
  "signing in or anything with a password: use sign-in-form, and never ask for a password in a generated form",
  "confirming an action that pays, sends or deletes: use approval-prompt after the form"
 ],
 "type": "form",
 "style": "quiet",
 "width": "narrow",
 "tags": {
  "audience": "work",
  "industries": [
   "any"
  ],
  "keywords": [
   "form",
   "fields",
   "input",
   "validation",
   "request"
  ]
 },
 "root": ".odl-form-fields",
 "parts": [
  {
   "name": "title",
   "selector": ".odl-form-fields h3",
   "what": "what the form is for",
   "slot": "text"
  },
  {
   "name": "intro",
   "selector": ".odl-intro",
   "what": "one sentence of context, such as a deadline",
   "slot": "text"
  },
  {
   "name": "fields",
   "selector": ".odl-field",
   "what": "one field: label, control, hint and error",
   "slot": "list"
  },
  {
   "name": "label",
   "selector": ".odl-name",
   "what": "the field's name; 'Optional' is added when it is not required",
   "slot": "text"
  },
  {
   "name": "control",
   "selector": ".odl-input",
   "what": "the native input chosen by kind: text, number, select or date, or a switch for toggle",
   "slot": "none"
  },
  {
   "name": "hint",
   "selector": ".odl-hint",
   "what": "one line that helps fill it in",
   "slot": "text"
  },
  {
   "name": "error",
   "selector": ".odl-error",
   "what": "what to fix, written by the component's own check",
   "slot": "none"
  },
  {
   "name": "cancel",
   "selector": ".odl-form-fields footer .odl-btn:not(.odl-submit)",
   "what": "leaves things as they are",
   "slot": "action"
  },
  {
   "name": "submit",
   "selector": ".odl-submit",
   "what": "sends the answers to the assistant once every field passes; effect none, because the form only collects details: anything it then books, pays or sends is confirmed in its own approval step",
   "slot": "action"
  }
 ],
 "data": {
  "type": "object",
  "required": [
   "title",
   "fields",
   "submit"
  ],
  "properties": {
   "title": {
    "type": "string",
    "maxLength": 48,
    "part": "title"
   },
   "intro": {
    "type": "string",
    "maxLength": 120,
    "part": "intro"
   },
   "fields": {
    "type": "array",
    "minItems": 1,
    "maxItems": 8,
    "part": "fields",
    "items": {
     "type": "object",
     "required": [
      "name",
      "label",
      "kind"
     ],
     "properties": {
      "name": {
       "type": "string",
       "maxLength": 40,
       "part": "control"
      },
      "label": {
       "type": "string",
       "maxLength": 40,
       "part": "label"
      },
      "kind": {
       "type": "string",
       "enum": [
        "text",
        "number",
        "select",
        "date",
        "toggle"
       ],
       "part": "control"
      },
      "hint": {
       "type": "string",
       "maxLength": 90,
       "part": "hint"
      },
      "required": {
       "type": "boolean",
       "part": "label"
      },
      "value": {
       "type": [
        "string",
        "number",
        "boolean"
       ],
       "maxLength": 120,
       "part": "control"
      },
      "min": {
       "type": "number",
       "part": "control"
      },
      "max": {
       "type": "number",
       "part": "control"
      },
      "options": {
       "type": "array",
       "maxItems": 12,
       "part": "control",
       "items": {
        "type": "object",
        "required": [
         "label",
         "value"
        ],
        "properties": {
         "label": {
          "type": "string",
          "maxLength": 60
         },
         "value": {
          "type": "string",
          "maxLength": 60
         }
        }
       }
      }
     }
    }
   },
   "submit": {
    "type": "object",
    "part": "submit",
    "properties": {
     "label": {
      "type": "string",
      "maxLength": 40
     },
     "event": {
      "type": "object"
     }
    },
    "required": [
     "label",
     "event"
    ]
   },
   "cancel": {
    "type": "object",
    "part": "cancel",
    "properties": {
     "label": {
      "type": "string",
      "maxLength": 40
     },
     "event": {
      "type": "object"
     }
    },
    "required": [
     "label",
     "event"
    ]
   }
  }
 },
 "states": {
  "shown": [
   "filled",
   "optional",
   "error",
   "switch on",
   "switch off",
   "hover",
   "focus",
   "disabled",
   "buttons stack on a phone"
  ],
  "edge": {
   "empty": {
    "status": "handled",
    "evidence": "data-od-list",
    "how": "the Smart UI renderer puts one plain 'Nothing to show yet.' line in an empty list"
   },
   "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": "a missing required value or a number out of range shows a message under the field, marks it aria-invalid and moves focus to the first one on Continue",
    "evidence": "aria-invalid"
   },
   "disabled": {
    "status": "shown",
    "how": "a field the person may not change is disabled and greyed; its value stays readable",
    "evidence": ".odl-input:disabled"
   },
   "overflow": {
    "status": "handled",
    "how": "fields stack; on a phone the two buttons stack full width with Continue on top",
    "evidence": "flex-direction: column-reverse"
   },
   "long_text": {
    "status": "handled",
    "how": "labels, hints and errors wrap; values scroll inside their control",
    "evidence": "overflow-wrap: anywhere"
   },
   "missing_image": {
    "status": "none",
    "how": "the part has no photo"
   }
  }
 },
 "tokens": [
  "--ds-border-width-default",
  "--ds-color-bg-action-primary",
  "--ds-color-bg-action-primary-hover",
  "--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-separator",
  "--ds-color-border-strong",
  "--ds-color-border-subtle",
  "--ds-color-surface-raised",
  "--ds-color-text-danger",
  "--ds-color-text-on-action",
  "--ds-color-text-primary",
  "--ds-color-text-secondary",
  "--ds-elevation-raised",
  "--ds-focus-ring-offset",
  "--ds-focus-ring-width",
  "--ds-font-weight-body",
  "--ds-font-weight-label",
  "--ds-icon-stroke-md",
  "--ds-motion-duration-short",
  "--ds-motion-easing-standard",
  "--ds-radius-container",
  "--ds-radius-control",
  "--ds-radius-full",
  "--ds-size-control-md",
  "--ds-size-icon-sm",
  "--ds-size-target-min",
  "--ds-size-target-pointer",
  "--ds-space-2",
  "--ds-space-inline-md",
  "--ds-space-inline-sm",
  "--ds-space-inset-lg",
  "--ds-space-inset-md",
  "--ds-space-inset-xl",
  "--ds-space-stack-lg",
  "--ds-space-stack-md",
  "--ds-space-stack-sm",
  "--ds-space-stack-xs",
  "--ds-text-body-md",
  "--ds-text-body-sm",
  "--ds-text-label-lg",
  "--ds-text-label-md",
  "--ds-text-title-md"
 ],
 "a11y": {
  "summary": "Each control sits inside its label; hints and error messages are linked with aria-describedby, a wrong field gets aria-invalid, and Continue moves focus to the first field to fix. Toggles are switches (role=switch).",
  "roles": [
   "banner",
   "contentinfo",
   "form",
   "switch"
  ],
  "keyboard": [
   "Tab moves through the fields, then Cancel and Continue",
   "Space turns a switch on or off; arrow keys move through a select's options",
   "Enter in a text field presses Continue, which checks every field first"
  ],
  "controls": {
   "button": 2,
   "input": 7,
   "link": 0,
   "select": 2,
   "textarea": 0,
   "summary": 0
  },
  "focus_visible": true,
  "reduced_motion": true,
  "min_target": true
 },
 "files": {
  "html": "components/form-fields/component.html",
  "bytes": 14968,
  "script": true
 },
 "source": null,
 "photos": 0,
 "urls": {
  "html": "https://opendesigner.dev/library/form-fields.html",
  "json": "https://opendesigner.dev/library/form-fields.json",
  "page": "https://opendesigner.dev/library/form-fields/"
 },
 "get": {
  "curl": "curl -fsSLo form-fields.html https://opendesigner.dev/library/form-fields.html",
  "opendesigner": "python3 path/to/skills/opendesigner/scripts/library.py add form-fields --from https://opendesigner.dev",
  "prompt": "Add the OpenDesigner component form-fields to this project: https://opendesigner.dev/library/form-fields.html. Keep its markup and states; map its --ds-* variables to my tokens."
 }
}
