j3w1 / theme

j3w1 / theme4.0.0

j3w1 / command

  • SPEC Overview
  • SPEC Foundations
  • SPEC Components
  • SPEC Patterns
  • SPEC Tokens
  • SPEC Tools
  • SPEC Workbench
  • SPEC Form builder
  • SPEC Task kit
  • SPEC Recipes
  • SPEC Figma bridge
  • SPEC Report an issue
  • SPEC Implement
  • SPEC For agents
  • SPEC Ports
  • SPEC Releases
  • SPEC Specification
  • DEMO Vue application demo

File input

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.

stable · light DOM · 4.0.0. Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.

Live variants

default

No file chosen

Choose local files. This component does not upload them; the host validates its own file policy.

✕ Choose at least one file.

Markup
<j3w1-file-input id="example-file-input-default"><div class="file-input">
  <label class="file-input-label" for="example-file-input-default-fi-attach">Attachments</label>
  <div class="file-input-root">
    <input class="file-input-native" id="example-file-input-default-fi-attach" type="file" multiple="" aria-describedby="example-file-input-default-fi-attach-help example-file-input-default-fi-attach-message" name="example-file-input-default-fi-attach">
    <label class="file-input-button" for="example-file-input-default-fi-attach">Choose files</label>
    <span class="file-input-status" role="status">No file chosen</span>
    <span class="file-input-drop-hint" aria-hidden="true">Release to add</span>
  </div>
  <div class="file-input-progress" aria-hidden="true"></div>
  <p class="file-input-help" id="example-file-input-default-fi-attach-help">Choose local files. This component does not upload them; the host validates its own file policy.</p>
  <p class="file-input-message" id="example-file-input-default-fi-attach-message">✕ Choose at least one file.</p>
</div></j3w1-file-input>

drop-zone

No file chosen

Choose local files. This component does not upload them; the host validates its own file policy.

✕ Only PNG or WebP files.

Markup
<j3w1-file-input id="example-file-input-drop-zone"><div class="file-input">
  <label class="file-input-label" for="example-file-input-drop-zone-fi-zone">Screenshots</label>
  <div class="file-input-root file-input-zone">
    <input class="file-input-native" id="example-file-input-drop-zone-fi-zone" type="file" multiple="" accept="image/png,image/webp" aria-describedby="example-file-input-drop-zone-fi-zone-help example-file-input-drop-zone-fi-zone-message" name="example-file-input-drop-zone-fi-zone">
    <label class="file-input-button" for="example-file-input-drop-zone-fi-zone">Choose files</label>
    <span class="file-input-status" role="status">No file chosen</span>
    <span class="file-input-drop-hint" aria-hidden="true">Release to add</span>
  </div>
  <div class="file-input-progress" aria-hidden="true"></div>
  <p class="file-input-help" id="example-file-input-drop-zone-fi-zone-help">Choose local files. This component does not upload them; the host validates its own file policy.</p>
  <p class="file-input-message" id="example-file-input-drop-zone-fi-zone-message">✕ Only PNG or WebP files.</p>
</div></j3w1-file-input>

with-list

No file chosen

    Choose local files. This component does not upload them; the host validates its own file policy.

    ✕ One file could not be added.

    Markup
    <j3w1-file-input id="example-file-input-with-list"><div class="file-input">
      <label class="file-input-label" for="example-file-input-with-list-fi-list">Attachments</label>
      <div class="file-input-root">
        <input class="file-input-native" id="example-file-input-with-list-fi-list" type="file" multiple="" aria-describedby="example-file-input-with-list-fi-list-help example-file-input-with-list-fi-list-message" name="example-file-input-with-list-fi-list">
        <label class="file-input-button" for="example-file-input-with-list-fi-list">Add files</label>
        <span class="file-input-status" role="status">No file chosen</span>
        <span class="file-input-drop-hint" aria-hidden="true">Release to add</span>
      </div>
      <div class="file-input-progress" aria-hidden="true"></div>
      <ul class="file-input-list" aria-label="Chosen files"></ul>
      <p class="file-input-help" id="example-file-input-with-list-fi-list-help">Choose local files. This component does not upload them; the host validates its own file policy.</p>
      <p class="file-input-message" id="example-file-input-with-list-fi-list-message">✕ One file could not be added.</p>
    </div></j3w1-file-input>

    Install and register

    Use the exact version. If npm publication is pending, install the release tarball from the installation guide.

    npm install --save-exact @j3w1/ui@4.0.0
    import '@j3w1/ui/tokens.css';
    import '@j3w1/ui/styles/file-input.css';
    import '@j3w1/ui/register/file-input';

    Insert the maintained markup above after loading these imports. Its native children provide accessible names and form behavior; the empty custom tag alone does not create a control.

    Vue SFC, React, Astro and plain HTML integration →

    Copy the complete implementation

    npx --package=@j3w1/ui@4.0.0 j3w1-ui copy file-input --out ./vendor/j3w1/file-input

    The CLI verifies every file before writing and refuses overwrites. Serve the output over HTTP and open index.html. Keep its modules, CSS, tokens and licence notices together.

    Public API

    attributes

    NameType / signatureMeaning
    disabledCustomEventDisable the native controls owned by this instance.
    loadingCustomEventMark the primary control busy and prevent duplicate activation.

    properties

    NameType / signatureMeaning
    disabledbooleanDelegates disabled state to native children.
    namestringNative control name; application form keys remain explicit.
    filesFile[]Current native files; files are never uploaded by the component. Read only.

    methods

    NameType / signatureMeaning
    refresh(): voidReconnect behavior after replacing native child structure.
    focus(options?: FocusOptions): voidFocus the first usable native control.
    clear(): voidClear the current native file selection.

    events

    NameType / signatureMeaning
    j3w1-changeCustomEventNative file selection changed; read files from the component.

    Dependencies and verification

    Registered components: none. Style dependencies: none.

    Package presence establishes an available implementation. Actual passes depend on the tested artifact and environment. Read specification execution evidence; read packed-consumer evidence; inspect the complete state matrix.

    Full implementation manifest, API and token dependency closure

    Canonical specification

    File input file-input

    • Family: Advanced forms
    • Maturity: stable
    • Priority: R1
    • Since: 0.1.0
    • Native control: native <input type=file> named by two <label>s; the drop zone is a plain region, never a role — APG pattern
    • specified
    • demonstrated

    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.

    Report an issue with this component

    Tokens

    Focus a role to inspect its values; follow its link for persistent usage and source details.

    PartRoleValueUse
    button.bgcolor.action.secondary.bgobservedusergb(0 0 0 / 0%)Secondary button has no fill.
    button.textcolor.action.secondary.textapproveduse#ffa2a7D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
    button.bordercolor.action.secondary.borderapproveduse#986f72D-041 action.secondary.border: color.primitive.modern.control.
    button.bg-hovercolor.action.secondary.hover-bgobserveduse#630f0dSecondary button hover.
    button.bg-pressedcolor.action.secondary.pressed-bgobserveduse#420f0cSecondary button pressed.
    button.ringcolor.interaction.focus.ringobserveduse#e539351px dashed focus ring on controls and rows.
    button.border-invalidcolor.status.danger.borderobserveduse#e53935Danger border, 2px on invalid inputs.
    button.text-disabledcolor.text.disabledapproveduse#a3676bD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
    button.border-disabledcolor.border.disabledapproveduse#482b2bD-041 border.disabled: color.primitive.modern.divider.
    button.bg-disabledcolor.interaction.disabled.bgapproveduse#0d0707D-041 interaction.disabled.bg: color.primitive.modern.panel.
    zone.bordercolor.interaction.drop-targetobserveduse#e53935Dashed drop-target border.
    zone.bg-drop-targetcolor.interaction.marqueeobservedusergb(145 20 16 / 12%)Drag marquee and ghost.
    zone.hintcolor.text.mutedapproveduse#d4868bD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
    label.textcolor.text.brightapproveduse#ffa2a7D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
    status.textcolor.text.mutedapproveduse#d4868bD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
    list.textcolor.text.defaultapproveduse#e99499D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
    list.dividercolor.border.dividerapproveduse#482b2bD-041 border.divider: color.primitive.modern.divider.
    list.sizecolor.text.subtleapproveduse#bd787dD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
    list.errorcolor.status.danger.textobserveduse#f07178D-041 readable error text on neutral surfaces and native warning tints.
    remove.textcolor.action.tertiary.textapproveduse#e99499D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
    remove.bg-hovercolor.action.tertiary.hover-bgobserveduse#1c0a09Ghost and icon button hover.
    progress.trackcolor.interaction.pressed.bgobserveduse#420f0cPressed fill.
    progress.stripecolor.action.primary.bgapproveduse#7d1310Primary button fill; D-026 darkened it off color12, which stays fixed as heritage ANSI 12.
    help.textcolor.text.mutedapproveduse#d4868bD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
    message.textcolor.status.danger.textobserveduse#f07178D-041 readable error text on neutral surfaces and native warning tints.

    State matrix

    Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).

    StateButtonDrop zoneWith chosen files
    default
    No file chosen

    Up to five files, 10 MB each.

    ✕ Choose at least one file.

    or drop PNG or WebP files here

    PNG or WebP, 4 MB each.

    ✕ Only PNG or WebP files.

    3 files chosen
    • theme.json 12 KB
    • wallpaper.psd 48 MB ✕ Larger than 10 MB.
    • i3-config 3 KB

    Up to five files, 10 MB each.

    ✕ One file could not be added.

    hover
    focus-visible
    drop-target
    invalid
    disabled
    loading

    Purpose

    Choosing one or more files from the host. The native input keeps the host chooser, its keyboard and its security model; the theme replaces only its rendering.

    Anatomy

    Parts as listed in the anatomy; help and message sit below the root.

    States

    StateVisualNon-colour channel
    defaultbutton: 1px color.action.secondary.border outline, text color.action.secondary.text; zone: 1px dashed color.interaction.drop-target—
    hoverbutton → color.action.secondary.hover-bg; border unchangedcursor: pointer
    focus-visible1px dashed color.interaction.focus.ring at −2px on the button (the input has focus)the ring
    drop-targetroot fill color.interaction.marquee; 1px dashed color.interaction.drop-target borderdashed pattern; hint reads “Release to add”
    invalidbutton border 2px color.status.danger.border; message with ✕; a failed row shows its own ✕border 1 → 2px; glyphs; aria-invalid
    disabledbutton text color.text.disabled, border color.border.disabled, fill color.interaction.disabled.bg; zone dashes color.border.disabled; no hoverdisabled; cursor: not-allowed
    loadingstatic 45° stripes, color.action.primary.bg on color.interaction.pressed.bg, below the row; the button stays enabledaria-busy; status reads “Uploading n files”; no animation

    Precedence: disabled > loading > invalid > drop-target > hover; focus-visible is always drawn.

    Keyboard

    As in the key table; closing the chooser returns focus to the input. Dropping is pointer-only and never the sole way to add a file.

    Accessibility

    Hide the input with sr-only, never display: none or visibility: hidden, so it keeps focus and the chooser. Both labels name it (“Attachments”, “Choose files”); aria-describedby lists help and message. The status and list are announced through a polite live region after a choice; per-file errors are in the row text. The drop zone has no role and no tabindex. Text meets 4.5:1; borders 3:1.

    Portability

    The button is the secondary button of the actions family; the stripes are a repeating gradient or a hatched brush. Toolkits without drag-and-drop omit the zone and keep the button; without a translucent fill, composite the marquee over the canvas and record the value.

    Non-examples

    display: none on the input and a button that clicks it by script. A drop zone as the only way to add files. A rounded, dotted or pulsing zone. A spinner, or animated stripes, while uploading. Errors shown by a red file name alone. Disabling by opacity.

    Contrast report

    18 declared pairs measured on resolved sRGB values with the WCAG 2.x formula; 0 waived (decorative only), 0 failing. A pass is a design target for the named role, not an application-wide conformance claim.

    PairStateKindForegroundBackgroundRatioMinResult
    file-input default textdefaulttextcolor.action.secondary.text #ffa2a7color.surface.canvas #00000010.984.5pass
    file-input default borderdefaultuicolor.action.secondary.border #986f72color.surface.canvas #0000004.853pass
    file-input hover texthovertextcolor.action.secondary.text #ffa2a7color.action.secondary.hover-bg #630f0d6.844.5pass
    file-input focus-visible textfocus-visibletextcolor.action.secondary.text #ffa2a7color.surface.canvas #00000010.984.5pass
    file-input focus-visible borderfocus-visibleuicolor.action.secondary.border #986f72color.surface.canvas #0000004.853pass
    file-input focus-visible outlinefocus-visibleuicolor.interaction.focus.ring #e53935color.surface.canvas #0000004.973pass
    file-input drop-target textdrop-targettextcolor.text.default #e99499color.interaction.marquee #1102028.844.5pass
    file-input drop-target borderdrop-targetuicolor.interaction.drop-target #e53935color.interaction.marquee #1102024.803pass
    file-input invalid textinvalidtextcolor.action.secondary.text #ffa2a7color.surface.canvas #00000010.984.5pass
    file-input invalid borderinvaliduicolor.status.danger.border #e53935color.surface.canvas #0000004.973pass
    hovered button boundary against the canvas outside ithoveruicolor.action.secondary.border #986f72color.surface.canvas #0000004.853pass
    drop-zone dashed border at rest—uicolor.interaction.drop-target #e53935color.surface.canvas #0000004.973pass
    status, hint and help text on the canvas—textcolor.text.muted #d4868bcolor.surface.canvas #0000007.584.5pass
    file sizes on the canvas—textcolor.text.subtle #bd787dcolor.surface.canvas #0000006.154.5pass
    per-file error and validation message on the canvas—textcolor.status.danger.text #f07178color.surface.canvas #0000007.344.5pass
    disabled button text (exempt; house floor 3:1)disableduicolor.text.disabled #a3676bcolor.interaction.disabled.bg #0d07074.483pass
    disabled border (exempt)disableduicolor.border.disabled #482b2bcolor.interaction.disabled.bg #0d07071.581pass
    progress stripes (decorative pattern)loadinguicolor.action.primary.bg #7d1310color.interaction.pressed.bg #420f0c1.511pass

    Keys

    KeyAction
    Tab / Shift+TabReaches the native input (drawn as the button), then each remove button in the list.
    Enter / SpaceOpens the host file chooser from the input.
    Delete / BackspaceOn a focused remove button, same as activating it.
    EscapeCloses the host chooser (host behaviour).

    For agents

    Component JSON ·Implementation brief ·Build a task kit ·

    Related: button text-field progress list field

    Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-OVERFLOW FX-STATE-MATRIX