{
  "schemaVersion": 1,
  "theme": "j3w1-theme",
  "version": "1.1.0",
  "profile": "default",
  "profileStatus": "approved",
  "release": {
    "version": "1.1.0",
    "stability": "release",
    "policy": "Use the pinned approved default profile. Pending roles in that profile use-and-report their decision IDs; this does not approve them. Proposed profiles are preview-only and blocked for delivery. Heritage profiles are historical-only. Deprecated or heritage roles are blocked for new approved-profile mappings. Consume roles within their documented scope, never primitives. Release numbering does not approve profiles or tokens."
  },
  "sourceDigest": "sha256-C+iD5Q7l0XPU/boMj003wPbPLBOq3C3ZM7s4OfzJ6mc=",
  "id": "file-input",
  "family": "forms-advanced",
  "maturity": "stable",
  "priority": "R1",
  "coverage": {
    "family": "forms-advanced",
    "priority": "R1",
    "maturity": "stable",
    "specified": true,
    "demonstrated": true,
    "tested": false,
    "testImplemented": false,
    "states": 7
  },
  "title": "File input",
  "summary": "A native file input behind a label styled as a secondary button, optionally inside a drop zone, with a list of chosen files and per-file errors.",
  "anchors": {
    "site": "https://j3w1.github.io/theme/reference/#c-file-input",
    "spec": "spec/components/file-input.md",
    "brief": "exports/components/file-input.brief.txt"
  },
  "native": true,
  "aria": {
    "pattern": "native <input type=file> named by two <label>s; the drop zone is a plain region, never a role",
    "apg": "https://www.w3.org/WAI/ARIA/apg/practices/forms/"
  },
  "variants": [
    {
      "id": "default",
      "name": "Button"
    },
    {
      "id": "drop-zone",
      "name": "Drop zone"
    },
    {
      "id": "with-list",
      "name": "With chosen files"
    }
  ],
  "sizes": [
    "compact",
    "comfortable"
  ],
  "states": [
    "default",
    "hover",
    "focus-visible",
    "drop-target",
    "invalid",
    "disabled",
    "loading"
  ],
  "tokens": {
    "button.bg": {
      "alias": "color.action.secondary.bg",
      "value": "rgb(0 0 0 / 0%)",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "button.text": {
      "alias": "color.action.secondary.text",
      "value": "#ffa2a7",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "button.border": {
      "alias": "color.action.secondary.border",
      "value": "#a3676b",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "button.bg-hover": {
      "alias": "color.action.secondary.hover-bg",
      "value": "#630f0d",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "button.bg-pressed": {
      "alias": "color.action.secondary.pressed-bg",
      "value": "#420f0c",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "button.ring": {
      "alias": "color.interaction.focus.ring",
      "value": "#e53935",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "button.border-invalid": {
      "alias": "color.status.danger.border",
      "value": "#e53935",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "button.text-disabled": {
      "alias": "color.text.disabled",
      "value": "#8a5559",
      "status": "approved",
      "deprecated": false,
      "decisionId": "D-003",
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "button.border-disabled": {
      "alias": "color.border.disabled",
      "value": "#7d1310",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "button.bg-disabled": {
      "alias": "color.interaction.disabled.bg",
      "value": "#160b0b",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "zone.border": {
      "alias": "color.interaction.drop-target",
      "value": "#e53935",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "zone.bg-drop-target": {
      "alias": "color.interaction.marquee",
      "value": "rgb(145 20 16 / 12%)",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "zone.hint": {
      "alias": "color.text.muted",
      "value": "#bd787d",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "label.text": {
      "alias": "color.text.bright",
      "value": "#ffa2a7",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "status.text": {
      "alias": "color.text.muted",
      "value": "#bd787d",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "list.text": {
      "alias": "color.text.default",
      "value": "#e99499",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "list.divider": {
      "alias": "color.border.divider",
      "value": "#2b0e0d",
      "status": "proposed",
      "deprecated": false,
      "decisionId": "D-008",
      "eligibility": {
        "action": "use-and-report",
        "reason": "Use the pinned value and disclose every pending decision ID; approval statuses are unchanged.",
        "decisionIds": [
          "D-008"
        ]
      }
    },
    "list.size": {
      "alias": "color.text.subtle",
      "value": "#ad7175",
      "status": "approved",
      "deprecated": false,
      "decisionId": "D-003",
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "list.error": {
      "alias": "color.status.danger.text",
      "value": "#f73f35",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "remove.text": {
      "alias": "color.action.tertiary.text",
      "value": "#e99499",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "remove.bg-hover": {
      "alias": "color.action.tertiary.hover-bg",
      "value": "#1c0a09",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "progress.track": {
      "alias": "color.interaction.pressed.bg",
      "value": "#420f0c",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "progress.stripe": {
      "alias": "color.action.primary.bg",
      "value": "#7d1310",
      "status": "approved",
      "deprecated": false,
      "decisionId": "D-026",
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "help.text": {
      "alias": "color.text.muted",
      "value": "#bd787d",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    },
    "message.text": {
      "alias": "color.status.danger.text",
      "value": "#f73f35",
      "status": "observed",
      "deprecated": false,
      "decisionId": null,
      "eligibility": {
        "action": "use",
        "reason": "Use within the role's documented scope and restrictions.",
        "decisionIds": []
      }
    }
  },
  "stateRules": [
    {
      "state": "default",
      "visual": "button: 1px {color.action.secondary.border} outline, text {color.action.secondary.text}; zone: 1px dashed {color.interaction.drop-target}",
      "channel": "—",
      "tokens": {
        "fg": {
          "alias": "color.action.secondary.text",
          "value": "#ffa2a7",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        },
        "bg": {
          "alias": "color.surface.canvas",
          "value": "#000000",
          "status": "approved",
          "deprecated": false,
          "decisionId": "D-023",
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        },
        "border": {
          "alias": "color.action.secondary.border",
          "value": "#a3676b",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        }
      }
    },
    {
      "state": "hover",
      "visual": "button background → {color.action.secondary.hover-bg}; border unchanged",
      "channel": "cursor: pointer",
      "tokens": {
        "fg": {
          "alias": "color.action.secondary.text",
          "value": "#ffa2a7",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        },
        "bg": {
          "alias": "color.action.secondary.hover-bg",
          "value": "#630f0d",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        }
      }
    },
    {
      "state": "focus-visible",
      "visual": "ring 1px dashed {color.interaction.focus.ring} at −2px on the button (the input has focus)",
      "channel": "the ring",
      "tokens": {
        "fg": {
          "alias": "color.action.secondary.text",
          "value": "#ffa2a7",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        },
        "bg": {
          "alias": "color.surface.canvas",
          "value": "#000000",
          "status": "approved",
          "deprecated": false,
          "decisionId": "D-023",
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        },
        "border": {
          "alias": "color.action.secondary.border",
          "value": "#a3676b",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        },
        "outline": {
          "alias": "color.interaction.focus.ring",
          "value": "#e53935",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        }
      }
    },
    {
      "state": "drop-target",
      "visual": "root fill {color.interaction.marquee}; border 1px dashed {color.interaction.drop-target}",
      "channel": "dashed pattern; the hint text changes to \"Release to add\"",
      "tokens": {
        "fg": {
          "alias": "color.text.default",
          "value": "#e99499",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        },
        "bg": {
          "alias": "color.interaction.marquee",
          "value": "rgb(145 20 16 / 12%)",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        },
        "border": {
          "alias": "color.interaction.drop-target",
          "value": "#e53935",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        }
      }
    },
    {
      "state": "invalid",
      "visual": "button border 2px {color.status.danger.border}; message with the ✕ glyph; a failed row shows its own ✕",
      "channel": "border width 1 → 2px; glyphs; `aria-invalid`",
      "tokens": {
        "fg": {
          "alias": "color.action.secondary.text",
          "value": "#ffa2a7",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        },
        "bg": {
          "alias": "color.surface.canvas",
          "value": "#000000",
          "status": "approved",
          "deprecated": false,
          "decisionId": "D-023",
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        },
        "border": {
          "alias": "color.status.danger.border",
          "value": "#e53935",
          "status": "observed",
          "deprecated": false,
          "decisionId": null,
          "eligibility": {
            "action": "use",
            "reason": "Use within the role's documented scope and restrictions.",
            "decisionIds": []
          }
        }
      }
    },
    {
      "state": "disabled",
      "visual": "button text {color.text.disabled}, border {color.border.disabled}, background {color.interaction.disabled.bg}; the zone's dashes take {color.border.disabled}; no hover",
      "channel": "`disabled`; cursor: not-allowed",
      "tokens": {}
    },
    {
      "state": "loading",
      "visual": "a static bar of 45° stripes, {color.action.primary.bg} on {color.interaction.pressed.bg}, below the row; the button stays enabled",
      "channel": "`aria-busy`; the status text reads \"Uploading n files\"; no animation",
      "tokens": {}
    }
  ],
  "keyboard": [
    {
      "key": "Tab / Shift+Tab",
      "action": "Reaches the native input (drawn as the button), then each remove button in the list."
    },
    {
      "key": "Enter / Space",
      "action": "Opens the host file chooser from the input."
    },
    {
      "key": "Delete / Backspace",
      "action": "On a focused remove button, same as activating it."
    },
    {
      "key": "Escape",
      "action": "Closes the host chooser (host behaviour)."
    }
  ],
  "responsive": "The button and status wrap onto two rows below 320px; the list rows truncate file names with an ellipsis and keep the size and remove button visible; the drop zone keeps its padding. RTL mirrors the button and status order and the list columns.",
  "portability": {
    "web": "native <input type=file> with the sr-only pattern (never display none, which removes it from the tab order); two <label for> elements name it; the drop zone listens for dragover and drop on the root and mirrors the drop-target attribute; progress through aria-busy on the root and a live status.",
    "nativeFallbacks": [
      "GTK4 FileDialog opened from a Button; the chosen list is a ListBox.",
      "Qt QFileDialog from a QPushButton; the list is a QListWidget.",
      "Terminal UI: a path line editor with completion; no drop zone."
    ]
  },
  "fixtures": [
    "FX-LONG",
    "FX-320",
    "FX-ZOOM-200",
    "FX-I18N",
    "FX-RTL",
    "FX-RM",
    "FX-HC",
    "FX-OVERFLOW",
    "FX-STATE-MATRIX"
  ],
  "related": [
    "button",
    "text-field",
    "progress",
    "list",
    "field"
  ],
  "specimens": [
    "admin-form"
  ],
  "nonExamples": "A hidden input with `display: none` and a button that clicks it by script. A\ndrop zone that is the only way to add files. A rounded, dotted, animated\n\"pulsing\" zone. A spinner while uploading. A progress bar that animates its\nstripes. Errors shown by a red file name alone. Disabling by opacity.",
  "contrast": [
    {
      "label": "file-input default text",
      "state": "default",
      "kind": "text",
      "min": 4.5,
      "fg": "color.action.secondary.text",
      "bg": "color.surface.canvas",
      "fgHex": "#ffa2a7",
      "bgHex": "#000000",
      "ratio": 10.98,
      "pass": true,
      "waiver": null
    },
    {
      "label": "file-input default border",
      "state": "default",
      "kind": "ui",
      "min": 3,
      "fg": "color.action.secondary.border",
      "bg": "color.surface.canvas",
      "fgHex": "#a3676b",
      "bgHex": "#000000",
      "ratio": 4.71,
      "pass": true,
      "waiver": null
    },
    {
      "label": "file-input hover text",
      "state": "hover",
      "kind": "text",
      "min": 4.5,
      "fg": "color.action.secondary.text",
      "bg": "color.action.secondary.hover-bg",
      "fgHex": "#ffa2a7",
      "bgHex": "#630f0d",
      "ratio": 6.84,
      "pass": true,
      "waiver": null
    },
    {
      "label": "file-input focus-visible text",
      "state": "focus-visible",
      "kind": "text",
      "min": 4.5,
      "fg": "color.action.secondary.text",
      "bg": "color.surface.canvas",
      "fgHex": "#ffa2a7",
      "bgHex": "#000000",
      "ratio": 10.98,
      "pass": true,
      "waiver": null
    },
    {
      "label": "file-input focus-visible border",
      "state": "focus-visible",
      "kind": "ui",
      "min": 3,
      "fg": "color.action.secondary.border",
      "bg": "color.surface.canvas",
      "fgHex": "#a3676b",
      "bgHex": "#000000",
      "ratio": 4.71,
      "pass": true,
      "waiver": null
    },
    {
      "label": "file-input focus-visible outline",
      "state": "focus-visible",
      "kind": "ui",
      "min": 3,
      "fg": "color.interaction.focus.ring",
      "bg": "color.surface.canvas",
      "fgHex": "#e53935",
      "bgHex": "#000000",
      "ratio": 4.97,
      "pass": true,
      "waiver": null
    },
    {
      "label": "file-input drop-target text",
      "state": "drop-target",
      "kind": "text",
      "min": 4.5,
      "fg": "color.text.default",
      "bg": "color.interaction.marquee",
      "fgHex": "#e99499",
      "bgHex": "#110202",
      "ratio": 8.84,
      "pass": true,
      "waiver": null
    },
    {
      "label": "file-input drop-target border",
      "state": "drop-target",
      "kind": "ui",
      "min": 3,
      "fg": "color.interaction.drop-target",
      "bg": "color.interaction.marquee",
      "fgHex": "#e53935",
      "bgHex": "#110202",
      "ratio": 4.8,
      "pass": true,
      "waiver": null
    },
    {
      "label": "file-input invalid text",
      "state": "invalid",
      "kind": "text",
      "min": 4.5,
      "fg": "color.action.secondary.text",
      "bg": "color.surface.canvas",
      "fgHex": "#ffa2a7",
      "bgHex": "#000000",
      "ratio": 10.98,
      "pass": true,
      "waiver": null
    },
    {
      "label": "file-input invalid border",
      "state": "invalid",
      "kind": "ui",
      "min": 3,
      "fg": "color.status.danger.border",
      "bg": "color.surface.canvas",
      "fgHex": "#e53935",
      "bgHex": "#000000",
      "ratio": 4.97,
      "pass": true,
      "waiver": null
    },
    {
      "label": "hovered button boundary against the canvas outside it",
      "state": "hover",
      "kind": "ui",
      "min": 3,
      "fg": "color.action.secondary.border",
      "bg": "color.surface.canvas",
      "fgHex": "#a3676b",
      "bgHex": "#000000",
      "ratio": 4.71,
      "pass": true,
      "waiver": "inside the hover fill the control border measures 2.94:1; the boundary is judged against the canvas on its outer side (4.45:1), and the fill change is itself the hover signal"
    },
    {
      "label": "drop-zone dashed border at rest",
      "state": null,
      "kind": "ui",
      "min": 3,
      "fg": "color.interaction.drop-target",
      "bg": "color.surface.canvas",
      "fgHex": "#e53935",
      "bgHex": "#000000",
      "ratio": 4.97,
      "pass": true,
      "waiver": null
    },
    {
      "label": "status, hint and help text on the canvas",
      "state": null,
      "kind": "text",
      "min": 4.5,
      "fg": "color.text.muted",
      "bg": "color.surface.canvas",
      "fgHex": "#bd787d",
      "bgHex": "#000000",
      "ratio": 6.15,
      "pass": true,
      "waiver": null
    },
    {
      "label": "file sizes on the canvas",
      "state": null,
      "kind": "text",
      "min": 4.5,
      "fg": "color.text.subtle",
      "bg": "color.surface.canvas",
      "fgHex": "#ad7175",
      "bgHex": "#000000",
      "ratio": 5.4,
      "pass": true,
      "waiver": null
    },
    {
      "label": "per-file error and validation message on the canvas",
      "state": null,
      "kind": "text",
      "min": 4.5,
      "fg": "color.status.danger.text",
      "bg": "color.surface.canvas",
      "fgHex": "#f73f35",
      "bgHex": "#000000",
      "ratio": 5.72,
      "pass": true,
      "waiver": null
    },
    {
      "label": "disabled button text (exempt; house floor 3:1)",
      "state": "disabled",
      "kind": "ui",
      "min": 3,
      "fg": "color.text.disabled",
      "bg": "color.interaction.disabled.bg",
      "fgHex": "#8a5559",
      "bgHex": "#160b0b",
      "ratio": 3.24,
      "pass": true,
      "waiver": null
    },
    {
      "label": "disabled border (exempt)",
      "state": "disabled",
      "kind": "ui",
      "min": 1,
      "fg": "color.border.disabled",
      "bg": "color.interaction.disabled.bg",
      "fgHex": "#7d1310",
      "bgHex": "#160b0b",
      "ratio": 1.82,
      "pass": true,
      "waiver": "disabled controls are exempt from 1.4.11"
    },
    {
      "label": "progress stripes (decorative pattern)",
      "state": "loading",
      "kind": "ui",
      "min": 1,
      "fg": "color.action.primary.bg",
      "bg": "color.interaction.pressed.bg",
      "fgHex": "#7d1310",
      "bgHex": "#420f0c",
      "ratio": 1.51,
      "pass": true,
      "waiver": "the stripes are a pattern, not a boundary; the loading state is carried by aria-busy and the visible status text"
    }
  ]
}
