{
 "id": "page-offline",
 "name": "Offline page",
 "purpose": "What a person sees with no connection: that they are offline, what still works on this device, and what waits until they're back.",
 "use_when": [
  "when the app has no network connection and the next screen needs one",
  "the browser or device reports no connection",
  "requests time out repeatedly and cached data exists"
 ],
 "avoid_when": [
  "the server is up but failing: use page-error",
  "only one feature needs the network: show a warning banner in that place instead (page-banners)"
 ],
 "type": "states",
 "style": "quiet",
 "width": "wide",
 "tags": {
  "audience": "work",
  "industries": [
   "any"
  ],
  "keywords": [
   "offline",
   "no connection",
   "network",
   "sync",
   "retry"
  ]
 },
 "root": ".odl-page-offline",
 "parts": [
  {
   "name": "title",
   "selector": ".odl-page-offline [data-od-slot=\"title\"]",
   "what": "the heading: what happened, in plain words, as in “You're offline”",
   "slot": "text"
  },
  {
   "name": "message",
   "selector": ".odl-page-offline [data-od-slot=\"message\"]",
   "what": "one or two sentences: what it means and what to do next, as in “Check your Wi-Fi or mobile data. We'll reconnect”",
   "slot": "text"
  },
  {
   "name": "works",
   "selector": ".odl-page-offline [data-od-list=\"works\"]",
   "what": "the works, one item each",
   "slot": "list"
  },
  {
   "name": "works.text",
   "selector": ".odl-page-offline [data-od-list=\"works\"] [data-od-slot=\"text\"]",
   "what": "the text, as in “Read the 24 notes you opened this week”",
   "slot": "text"
  },
  {
   "name": "waits",
   "selector": ".odl-page-offline [data-od-list=\"waits\"]",
   "what": "the waits, one item each",
   "slot": "list"
  },
  {
   "name": "waits.text",
   "selector": ".odl-page-offline [data-od-list=\"waits\"] [data-od-slot=\"text\"]",
   "what": "the text, as in “Sharing, comments and new files from your team”",
   "slot": "text"
  },
  {
   "name": "reconnect",
   "selector": ".odl-page-offline [data-od-action=\"reconnect\"]",
   "what": "the Try again now action",
   "slot": "action"
  },
  {
   "name": "synced",
   "selector": ".odl-page-offline [data-od-slot=\"synced\"]",
   "what": "when the device last synced, as in “Last synced today at 10:42 AM”",
   "slot": "text"
  }
 ],
 "data": {
  "type": "object",
  "properties": {
   "title": {
    "type": "string",
    "maxLength": 24,
    "part": "title"
   },
   "message": {
    "type": "string",
    "maxLength": 120,
    "part": "message"
   },
   "works": {
    "type": "array",
    "minItems": 1,
    "maxItems": 6,
    "items": {
     "type": "object",
     "properties": {
      "text": {
       "type": "string",
       "maxLength": 56,
       "part": "works.text"
      }
     },
     "required": [
      "text"
     ]
    },
    "part": "works"
   },
   "waits": {
    "type": "array",
    "minItems": 1,
    "maxItems": 4,
    "items": {
     "type": "object",
     "properties": {
      "text": {
       "type": "string",
       "maxLength": 72,
       "part": "waits.text"
      }
     },
     "required": [
      "text"
     ]
    },
    "part": "waits"
   },
   "synced": {
    "type": "string",
    "maxLength": 40,
    "part": "synced"
   },
   "reconnect": {
    "type": "object",
    "required": [
     "label",
     "event"
    ],
    "properties": {
     "label": {
      "type": "string",
      "maxLength": 40
     },
     "event": {
      "type": "object"
     }
    }
   }
  },
  "required": [
   "title",
   "message",
   "works",
   "waits",
   "synced"
  ]
 },
 "states": {
  "shown": [
   "offline",
   "checking",
   "back online",
   "hover",
   "focus"
  ],
  "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": "shown",
    "how": "Try again now shows a spinner while it checks the connection.",
    "evidence": "is-busy"
   },
   "error": {
    "status": "shown",
    "how": "The part is itself an error or system state, written in plain words with a next step.",
    "evidence": "class=\"odl-lead\""
   },
   "disabled": {
    "status": "shown",
    "how": "The button is aria-disabled while it checks.",
    "evidence": "aria-disabled"
   },
   "overflow": {
    "status": "handled",
    "how": "Actions stack full width under 30rem; the panel caps its width.",
    "evidence": "@container"
   },
   "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-neutral-subtle",
  "--ds-color-border-default",
  "--ds-color-border-focus",
  "--ds-color-border-subtle",
  "--ds-color-icon-default",
  "--ds-color-icon-subtle",
  "--ds-color-surface-base",
  "--ds-color-surface-raised",
  "--ds-color-surface-sunken",
  "--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-heading",
  "--ds-font-weight-label",
  "--ds-icon-stroke-md",
  "--ds-icon-stroke-sm",
  "--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-lg",
  "--ds-size-control-md",
  "--ds-size-icon-lg",
  "--ds-size-icon-sm",
  "--ds-size-target-min",
  "--ds-space-inline-md",
  "--ds-space-inline-sm",
  "--ds-space-inset-lg",
  "--ds-space-inset-md",
  "--ds-space-inset-sm",
  "--ds-space-inset-xl",
  "--ds-space-section-md",
  "--ds-space-section-sm",
  "--ds-space-stack-lg",
  "--ds-space-stack-md",
  "--ds-space-stack-sm",
  "--ds-space-stack-xl",
  "--ds-space-stack-xs",
  "--ds-text-body-md",
  "--ds-text-body-sm",
  "--ds-text-headline-sm",
  "--ds-text-label-lg",
  "--ds-text-label-md"
 ],
 "a11y": {
  "summary": "The list of what still works is a real list; a polite status line reports each reconnect check and the last sync time.",
  "roles": [
   "list",
   "region",
   "status"
  ],
  "keyboard": [
   "Tab moves through the links and buttons in reading order",
   "Enter follows a link; Enter or Space presses a button"
  ],
  "controls": {
   "button": 1,
   "input": 0,
   "link": 0,
   "select": 0,
   "textarea": 0,
   "summary": 0
  },
  "focus_visible": true,
  "reduced_motion": true,
  "min_target": true
 },
 "files": {
  "html": "components/page-offline/component.html",
  "bytes": 9917,
  "script": true
 },
 "source": null,
 "photos": 0,
 "urls": {
  "html": "https://opendesigner.dev/library/page-offline.html",
  "json": "https://opendesigner.dev/library/page-offline.json",
  "page": "https://opendesigner.dev/library/page-offline/"
 },
 "get": {
  "curl": "curl -fsSLo page-offline.html https://opendesigner.dev/library/page-offline.html",
  "opendesigner": "python3 path/to/skills/opendesigner/scripts/library.py add page-offline --from https://opendesigner.dev",
  "prompt": "Add the OpenDesigner component page-offline to this project: https://opendesigner.dev/library/page-offline.html. Keep its markup and states; map its --ds-* variables to my tokens."
 }
}
