{
 "id": "inline-spinner",
 "name": "Inline spinner",
 "purpose": "A small spinner inside the control that is working, for waits of one to a few seconds: a Save button and a name check beside its field.",
 "use_when": [
  "for short waits the person started, such as saving a form or checking a name",
  "an action the person started takes about 1 to 10 seconds",
  "a background check on one field"
 ],
 "avoid_when": [
  "waits under about 300 ms: show nothing",
  "waits over about 10 seconds or with steps: use progress-steps",
  "a whole area loading: use a skeleton"
 ],
 "type": "states",
 "style": "quiet",
 "width": "narrow",
 "tags": {
  "audience": "work",
  "industries": [
   "any"
  ],
  "keywords": [
   "spinner",
   "loading",
   "button",
   "saving",
   "short wait"
  ]
 },
 "root": ".odl-inline-spinner",
 "parts": [
  {
   "name": "save",
   "selector": ".odl-inline-spinner [data-od-action=\"save\"]",
   "what": "the Save changes action",
   "slot": "action"
  },
  {
   "name": "label",
   "selector": ".odl-inline-spinner [data-od-slot=\"label\"]",
   "what": "the visible label, as in “Save changes”",
   "slot": "text"
  }
 ],
 "data": {
  "type": "object",
  "properties": {
   "save": {
    "type": "object",
    "required": [
     "label",
     "event"
    ],
    "properties": {
     "label": {
      "type": "string",
      "maxLength": 40
     },
     "event": {
      "type": "object"
     }
    }
   }
  }
 },
 "states": {
  "shown": [
   "idle",
   "saving",
   "saved",
   "checking",
   "available",
   "taken",
   "reduced motion"
  ],
  "edge": {
   "empty": {
    "status": "handled",
    "how": "An empty address shows no check and no message.",
    "evidence": "if (!input.value.trim()) return"
   },
   "loading": {
    "status": "shown",
    "how": "The spinner sits inside the button or beside the field that is working.",
    "evidence": "odl-spin"
   },
   "error": {
    "status": "shown",
    "how": "A taken address says so beside the field in the danger colour, with a cross.",
    "evidence": "is-bad"
   },
   "disabled": {
    "status": "shown",
    "how": "Save changes is aria-disabled while saving, so it can't be pressed twice.",
    "evidence": "aria-disabled"
   },
   "overflow": {
    "status": "handled",
    "how": "The field shrinks before the row overflows.",
    "evidence": "min-inline-size: 0"
   },
   "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-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-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-secondary",
  "--ds-color-text-success",
  "--ds-color-text-tertiary",
  "--ds-focus-ring-offset",
  "--ds-focus-ring-width",
  "--ds-font-weight-body",
  "--ds-font-weight-label",
  "--ds-icon-stroke-md",
  "--ds-motion-duration-long",
  "--ds-motion-duration-short",
  "--ds-motion-easing-linear",
  "--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-text",
  "--ds-text-body-md",
  "--ds-text-body-sm",
  "--ds-text-label-lg",
  "--ds-text-label-md"
 ],
 "a11y": {
  "summary": "The button stays in place and keeps focus, its label turns to Saving…, and it is aria-disabled while it works; a polite status says Saving and Changes saved, and the field check is read through its own status. Under reduced motion the spinner stops turning and the words still say what is happening.",
  "roles": [
   "form",
   "status"
  ],
  "keyboard": [
   "Tab reaches the address field, then Save changes",
   "Focus stays on Save changes while it saves"
  ],
  "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/inline-spinner/component.html",
  "bytes": 9245,
  "script": true
 },
 "source": null,
 "photos": 0,
 "urls": {
  "html": "https://opendesigner.dev/library/inline-spinner.html",
  "json": "https://opendesigner.dev/library/inline-spinner.json",
  "page": "https://opendesigner.dev/library/inline-spinner/"
 },
 "get": {
  "curl": "curl -fsSLo inline-spinner.html https://opendesigner.dev/library/inline-spinner.html",
  "opendesigner": "python3 path/to/skills/opendesigner/scripts/library.py add inline-spinner --from https://opendesigner.dev",
  "prompt": "Add the OpenDesigner component inline-spinner to this project: https://opendesigner.dev/library/inline-spinner.html. Keep its markup and states; map its --ds-* variables to my tokens."
 }
}
