Skip to the page
TACTILE UI

Kit part · File uploads · Inputs

Drop zone

An in-tray for files. A machined lip stands round a ribbed floor; the floor rises a little when a file is dragged over it, and a lamp on the lip lights. What goes in lands in the tray as a sheet of paper and is listed as printed rows, a refusal says why, and the key inside the tray is a real file input.

Element
<tui-drop-zone>
Needs
kit.css + kit.mjs
Rules
drop-zone.rules.md · 6
Finishes
Aluminium · Graphite · Porcelain · Black
Volumes
Tactile · Quiet
On this page
  1. Variants
  2. Every state, at once
  3. API
  4. Markup
  5. Accessibility
  6. How it’s built
  7. Rules
  8. Related parts
01

Variants

Drop files here

Any file, up to 5 MB.

Ready0 files
Empty

Drop files here

Ready2 of 6
  • site-plan.pdf1.2 MB
  • front-elevation.png312 KB
Files in the tray, and listed

Drop files here

Release to add0 files
A file over it

Drop files here

Ready1 of 6
  • site-plan.pdf1.2 MB
  • scan-0042.tiffscan-0042.tiff is a TIFF file. It takes PNG, JPEG and PDF.
A file turned away

Drop your avatar

PNG or JPEG, up to 2 MB.

Ready0 files
One file only

Drop files here

Full6 of 6
Full

Drop files here

Off0 files
Disabled
02

Every state, at once

Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.

Aluminium

PartReadyHolding filesFile over itKey focusFullDisabled
Drop zone

Drop files

Ready0 of 6

Drop files

Ready2 of 6

Drop files

Release0 of 6

Drop files

Ready0 of 6

Drop files

Full6 of 6

Drop files

Off0 of 6

Graphite

PartReadyHolding filesFile over itKey focusFullDisabled
Drop zone

Drop files

Ready0 of 6

Drop files

Ready2 of 6

Drop files

Release0 of 6

Drop files

Ready0 of 6

Drop files

Full6 of 6

Drop files

Off0 of 6
03

API

NameKindValuesWhat it does
<tui-drop-zone>elementname · accept · max-size · max-files · multipleThe tray. name: what the files post under. accept: extensions, type/* or exact types. max-size: bytes, or "512kb", "5mb". max-files: how many at most. multiple: more than one.
disabledattributenoneSwitches off the drop and the picker; the tray dims to 55% and the lip says Off.
.tui-drop-zone__tray · __floorclasslip · floorThe lip is a raised plate 6px round the floor. The floor is the ribbed recess and rises under a drag. Set --tui-dz-h to change its height.
label.tui-drop-zone__pick > input[type=file]elementinside the floorThe key and the real input inside it. Named by the key’s words, described by the hint. Enter or Space opens the picker.
.tui-drop-zone__lipclasslamp · state · countThe front edge. The lamp lights over a drag; the state reads Ready, Release to add, Full or Off; the count reads “2 of 6”.
data-overattributeset by scriptOn the element while a file is dragged over it. Set it yourself to hold the state.
.tui-drop-zone__sheets · __sheetclassmade by scriptThe files lying in the tray: a sheet of paper each, printed with its kind and name, the newest on top, the pile fanned a little (five at most). A sheet settles as it lands; one under the pointer comes to the top, and its × takes the file out. They cover the floor’s printing while the tray holds files (data-holding on the floor).
.tui-drop-zone__rowclassmade by scriptA printed row: extension plate, name, size and a key that removes it. With data-refused it has a red lamp and the reason in place of the size.
filespropertyarray of FileWhat is in the tray now.
add(files)methodarray of FileOffer files the way a drop does: the same checks, the same rows, the same event.
tui-fileseventdetail.files · added · removed · rejectedAfter every change. Bubbles. rejected holds { file, why, hint } for each file turned away.
fileSize · parseSize · acceptLabel · dropAccepts · dropTake · dropHint · dropSays · dropZoneHTML · dropSheetsHTMLfunctionkit.mjsThe rules, pure: sizes as words, an accept list as words, what a tray takes and refuses, what to say, and the markup for a page that renders on a server, the sheets included.
Enter · Spacekeyboardon the keyOpens the file picker.
04

Markup

<tui-drop-zone name="attachments" accept="image/png,image/jpeg,.pdf"
               max-size="5mb" max-files="6" multiple>
  <div class="tui-drop-zone__tray">
    <div class="tui-drop-zone__floor">
      <p class="tui-drop-zone__title">Drop files in the tray</p>
      <p class="tui-drop-zone__hint">Takes PNG, JPEG and PDF, up to 5 MB each. 6 files at most.</p>
      <label class="tui-key tui-drop-zone__pick">
        <input class="tui-drop-zone__input" type="file">Choose files
      </label>
    </div>
    <div class="tui-drop-zone__lip">
      <span class="tui-lamp" aria-hidden="true"></span>
      <span class="tui-drop-zone__state">Ready</span>
      <span class="tui-drop-zone__count"></span>
    </div>
  </div>
</tui-drop-zone>
import { dropZoneHTML } from '/kit/kit.mjs';   // also defines <tui-drop-zone>

// An empty <tui-drop-zone> builds the tray above for itself.
const zone = document.querySelector('tui-drop-zone[name="attachments"]');
zone.files;                       // the File objects in the tray
zone.add(someFiles);              // offer files as a drop would
zone.addEventListener('tui-files', (e) => {
  e.detail.added;                 // files that just went in
  e.detail.rejected;              // [{ file, why: 'type' | 'size' | 'duplicate' | 'full', hint }]
});

// A form posts exactly the files in the tray, under the element's name:
new FormData(form).getAll('attachments');

Needs kit.css and kit.mjs. How to install the kit.

05

Accessibility

  • The picker is a real <input type="file"> inside the key, so it sits in the tab order, is named by the key’s words and opens with Enter or Space. Nothing depends on dragging.
  • The hint is attached to the input with aria-describedby, so the types and the limit are read before anyone chooses.
  • Every addition, removal and refusal is said in a polite live region: what changed and how many files are in the tray now.
  • A refusal names the file and gives the rule in words. Its row stays on the list until it is dismissed, and its red lamp is a second signal beside the words.
  • The lit lamp and the raised floor say a drag is over the tray; the word beside the lamp (“Release to add”) says it too, so neither colour nor movement carries it alone.
  • Each row’s key names its file (“Remove site-plan.pdf”). After a file goes, focus lands on the next row’s key, or back on Choose files.
  • The sheets in the tray are a picture of the rows under it, so they are hidden from a screen reader and their × keys are out of the tab order: the rows carry the same files, and their keys do the same thing.
  • With reduced motion the floor changes depth at once instead of easing, and sheets are simply there, without settling.
06

How it’s built

The lip is a raised plate; the floor in it is the kit’s recess with ribs drawn across, each a dark line and a lit one, like the other grooves. Six pixels of lip round a 6px floor make the tray’s 12px corner concentric. Under a drag the floor’s depth is one number, --tui-dz-depth, eased from 1 to a quarter in 160ms: the shadows that make it a recess thin out and the floor sits up a pixel or two, which is all lifting a floor means. Drag events fire for every child under the pointer, so entering and leaving are counted. What comes in, from a drop or from the picker, goes through dropTake, which is pure and tested, and the accepted files are written back into the real input through a DataTransfer, so a form posts exactly what the tray shows. What was just accepted lands as paper: sheets in the kit’s paper tokens, each turned and shifted by its place in the pile, and a new one drops the last 18px and settles in 340ms, one after the other.

07

Rules

drop-zone.rules.mdIn the brain

A tray for files that a keyboard and a phone can use as well as a mouse.

6 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.

08

Field

Text entry in a recess, the label printed above it, and a hint that says how to fix an error.

Progress

Progress through a task: ink filling a groove, never a coloured bar.

  1. IntroPiano, room mics1:12
  2. LodeFull band4:05
  3. Night reelVocal and tape3:42

List

Rows in a rack: a mark, a title, a second line and a fact or a key at the end. Chosen rows sink into a recess with their check key latched; in a running order, a row lifts out of the rack on its skirt and slides past the others, from the keyboard or by its grip.

Docs
Finish
Volume