Skip to content

Dropzone

hc-dropzone decorates a native <input type="file"> with a drop surface. The interactive element stays the input: the wrapping <label> opens the picker on click, keyboard focus lands on the (visually hidden, still focusable) input, and required / accept / field-errors wiring attach to it as usual. Drag-and-drop is a pointer-only enhancement — a drop assigns dataTransfer.files to the input and fires a normal bubbling change, so everything downstream (form serialization, the file-upload recipe, validation) cannot tell the file wasn’t browsed.

.hc-dropzone__files shows the selected file names (set by the behavior on every change, dropped or browsed); it collapses when empty. The hint is authored markup — write it in your app’s language.

Included in the auto-init ./behaviors bundle. While a file drags over the zone it sets data-dragover (styled below); a drop assigns the files to the input — a single-file input takes only the first — and dispatches a bubbling change. Drags that carry no files (text selections) are ignored, as are zones whose input is disabled. No new events, no network.

StateSelectorEffect
Drag over[data-dragover]Solid accent border (--hc-dropzone-dragover-border, follows data-color) + tinted background.
Keyboard focus:focus-withinStandard focus ring (focus lives on the input).
Disabled:has(> .hc-dropzone__input:disabled)Muted, cursor: not-allowed; the behavior ignores drops.
Invalid:has(> .hc-dropzone__input[aria-invalid="true"])Error border — set by field-errors like any other control.

data-size="sm" tightens the padding for dense forms.

Uploading is the file-upload recipe’s contract: data-hx-encoding="multipart/form-data" on the form, live progress via installUploadProgress(), and the server’s response shape all live there. The dropzone drops into that form unchanged — a drop ends in the same native change as a browse.

  • Everything a keyboard or assistive-tech user needs is the native input: label activation, focus + Enter/Space, constraint validation, aria-invalid / aria-describedby from field-errors.
  • Drag-and-drop is an enhancement; every outcome is reachable via browse. No ARIA is invented.
  • Selected names are visible text inside the label. Pair with a toast if selection must be announced beyond that.
  • Non-goals (v1): paste-to-upload, directory drops.

Component tokens (in component.tokens.json) — all referencing semantic tokens, so themes, accents (data-color) and dark mode apply automatically:

Token pathPurpose
dropzone.bg / fgSurface background and text.
dropzone.borderDashed border color.
dropzone.hintHint text color.
dropzone.radiusCorner radius.
dropzone.padding-y / padding-xSurface padding (data-size="sm" overrides it).
dropzone.dragover-border / dragover-bgDrag-over accent (follows data-color).
dropzone.error-borderInvalid-state border.
Show the generated CSS variables
--hc-dropzone-bg | -fg | -border | -hint | -radius
--hc-dropzone-padding-y | -padding-x
--hc-dropzone-dragover-border | -dragover-bg
--hc-dropzone-error-border