j3w1/theme implementation brief — file-input
theme: j3w1-theme 1.1.0 profile: default sourceDigest: sha256-C+iD5Q7l0XPU/boMj003wPbPLBOq3C3ZM7s4OfzJ6mc=
pin: https://raw.githubusercontent.com/j3w1/theme/v1.1.0/exports/components/file-input.json
spec: spec/components/file-input.md site: https://j3w1.github.io/theme/reference/#c-file-input
maturity: stable priority: R1
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.
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.
Tokens (resolved):
button.bg rgb(0 0 0 / 0%) ({color.action.secondary.bg}) use
button.text #ffa2a7 ({color.action.secondary.text}) use
button.border #a3676b ({color.action.secondary.border}) use
button.bg-hover #630f0d ({color.action.secondary.hover-bg}) use
button.bg-pressed #420f0c ({color.action.secondary.pressed-bg}) use
button.ring #e53935 ({color.interaction.focus.ring}) use
button.border-invalid #e53935 ({color.status.danger.border}) use
button.text-disabled #8a5559 ({color.text.disabled}) use
button.border-disabled #7d1310 ({color.border.disabled}) use
button.bg-disabled #160b0b ({color.interaction.disabled.bg}) use
zone.border #e53935 ({color.interaction.drop-target}) use
zone.bg-drop-target rgb(145 20 16 / 12%) ({color.interaction.marquee}) use
zone.hint #bd787d ({color.text.muted}) use
label.text #ffa2a7 ({color.text.bright}) use
status.text #bd787d ({color.text.muted}) use
list.text #e99499 ({color.text.default}) use
list.divider #2b0e0d ({color.border.divider}) use-and-report (D-008)
list.size #ad7175 ({color.text.subtle}) use
list.error #f73f35 ({color.status.danger.text}) use
remove.text #e99499 ({color.action.tertiary.text}) use
remove.bg-hover #1c0a09 ({color.action.tertiary.hover-bg}) use
progress.track #420f0c ({color.interaction.pressed.bg}) use
progress.stripe #7d1310 ({color.action.primary.bg}) use
help.text #bd787d ({color.text.muted}) use
message.text #f73f35 ({color.status.danger.text}) use
States: default, hover, focus-visible, drop-target, invalid, disabled, loading
default: button: 1px {color.action.secondary.border} outline, text {color.action.secondary.text}; zone: 1px dashed {color.interaction.drop-target} [—]
hover: button background → {color.action.secondary.hover-bg}; border unchanged [cursor: pointer]
focus-visible: ring 1px dashed {color.interaction.focus.ring} at −2px on the button (the input has focus) [the ring]
drop-target: root fill {color.interaction.marquee}; border 1px dashed {color.interaction.drop-target} [dashed pattern; the hint text changes to "Release to add"]
invalid: button border 2px {color.status.danger.border}; message with the ✕ glyph; a failed row shows its own ✕ [border width 1 → 2px; glyphs; `aria-invalid`]
disabled: button text {color.text.disabled}, border {color.border.disabled}, background {color.interaction.disabled.bg}; the zone's dashes take {color.border.disabled}; no hover [`disabled`; cursor: not-allowed]
loading: a static bar of 45° stripes, {color.action.primary.bg} on {color.interaction.pressed.bg}, below the row; the button stays enabled [`aria-busy`; the status text reads "Uploading n files"; no animation]
Keyboard:
Tab / Shift+Tab: Reaches the native input (drawn as the button), then each remove button in the list.
Enter / Space: Opens the host file chooser from the input.
Delete / Backspace: On a focused remove button, same as activating it.
Escape: Closes the host chooser (host behaviour).
ARIA: native named by two