Skip to content

ExpressiveCSS

Browse documentation

Drop zone

A large target for choosing or dropping files, with a list of what was chosen.

View as Markdown

A .drop-zone is a large target for choosing files. Put an icon, a prompt and a native <input type="file"> inside a <label>. Add an optional .drop-zone-files list to show what was chosen.

The CSS stretches the input invisibly over the whole label. A click, Enter, Space or a dropped file reaches it exactly as it would a plain file input, and the label text names it. For a compact button and path field, use the file input.

    <div class="drop-zone">
      <label>
        <span class="material-symbols" aria-hidden="true">upload_file</span>
        Drop files here or browse
        <small>PDF or PNG files</small>
        <input type="file" multiple accept=".pdf,.png">
      </label>
      <ul class="drop-zone-files" aria-live="polite"></ul>
    </div>

    Behavior

    Add multiple to the input to take several files at once. A dropped file replaces the selection, as it does on any file input. The Forms behavior adds dragover to the zone while files are over it. It fills .drop-zone-files with each file's name and size, formatted for the page's locale. It listens on the document, so zones added later work too. Resetting the form empties the list after reset dispatch finishes; a canceled reset preserves it. A newer selection keeps its list and feedback.

    accept is a picker hint. Opt into the type validation below for matching checks on both browse and drop selections. Check received files on the server; metadata does not verify contents.

    Type validation

    Add data-validate-types to the native input to check every selected file against accept. One unsupported file invalidates the whole selection and blocks normal native form submission. Every selected file stays in the input and optional list. Unmarked zones keep their existing picker-hint behavior.

      <div class="drop-zone">
        <label>Drop documents here or browse
          <small>PDF or PNG files</small>
          <input type="file" multiple data-validate-types accept=".pdf,.png">
        </label>
        <p class="supporting-text" data-error="Choose PDF or PNG files."
           role="status" aria-live="polite" hidden></p>
        <ul class="drop-zone-files" aria-live="polite"></ul>
      </div>

      Match extensions such as .pdf or .tar.gz, exact MIME types without parameters, such as application/pdf, or image/*, audio/* and video/*. Comma-separated alternatives are trimmed and case-insensitive. Empty MIME types can match an explicit extension, but do not match MIME-only rules. Unsupported tokens are ignored; missing, empty or wholly unsupported accept values impose no type restriction.

      Supply the direct [data-error] status area even without a file list. Its data-error value customizes the type message; an empty value uses a message naming accept. The runtime writes real text, appends its id once to the input's aria-describedby, and updates aria-invalid without moving focus. Required fields and application custom errors remain effective. Existing application custom validity takes precedence over the native type message; the status can still explain a type mismatch.

      Valid replacement, clearing or an uncanceled reset releases only the framework's type error. Remaining native/application errors stay visible. Canceled resets preserve the current view. Disabled controls remain inactive; after enabling or changing files/constraints from script, call Expressive.Forms.validateField(input) to refresh validity and feedback silently. A bubbling composed input or change also refreshes the file list. Both paths work in open shadow roots.

      A changed invalid selection reports one bubbling, composed, noncancelable expressive:reject from the zone with { input, files, rejected, reason: 'type' }. File arrays are snapshots. A native input/change pair reports once; silent validation and reset report no rejection. This is a notification, so canceling it cannot allow invalid files. Combine type validation with the per-file size and count limits below. Uploads are outside these options. Native novalidate/formnovalidate bypasses remain native; always validate received files on the server.

      Size limit

      Add data-max-size="1048576" to limit each file to 1 MiB. A file exactly at the limit passes. One oversized file invalidates the whole selection and blocks ordinary native form submission; all files remain in the input and optional list. The live example checks size only.

        This markup adds type checks to the same size limit:

        <div class="drop-zone">
          <label>Drop documents here or browse
            <small>PDF or PNG, at most 1 MiB per file</small>
            <input type="file" multiple data-validate-types accept=".pdf,.png"
                   data-max-size="1048576">
          </label>
          <p class="supporting-text" data-error="Choose PDF or PNG files."
             data-error-size="Choose files no larger than 1 MiB each."
             role="status" aria-live="polite" hidden></p>
          <ul class="drop-zone-files" aria-live="polite"></ul>
        </div>

        Use positive decimal safe-integer bytes; surrounding whitespace is allowed. Missing, blank, zero, negative, fractional, exponent, hexadecimal, nonnumeric or unsafe values impose no size limit. This limits each file separately, not their combined size. A size-only input leaves accept as a picker hint; add data-validate-types for type checking.

        Reuse the direct polite [data-error] status area. data-error-size customizes the size message; an empty value uses a message naming the exact byte limit. If types fail, the type error takes precedence. Otherwise oversized files report { input, files, rejected, reason: 'size' } through the same noncancelable expressive:reject event. Its rejected snapshot contains only oversized files, while files contains the whole selection. Changing the type opt-in, accept or effective size limit can report again after a file input/change event; equivalent byte limits still deduplicate.

        Script refresh, application-validity ownership, disabled controls, native submission settings and reset recovery follow the type-validation contract above. Forms.validateField(input) silently refreshes both constraints. Always enforce received file limits on the server.

        File count

        Add data-max-files="2" to allow at most two selected files. Exactly two pass. A larger selection stays intact, but native validity blocks ordinary form submission and explains the count violation. This live example checks count independently of type and size.

          This markup combines type, per-file size and count checks:

          <div class="drop-zone">
            <label>Drop documents here or browse
              <small>Up to 2 PDF or PNG files, at most 1 MiB each</small>
              <input type="file" multiple data-validate-types accept=".pdf,.png"
                     data-max-size="1048576" data-max-files="2">
            </label>
            <p class="supporting-text" data-error="Choose PDF or PNG files."
               data-error-size="Choose files no larger than 1 MiB each."
               data-error-count="Choose no more than two files."
               role="status" aria-live="polite" hidden></p>
            <ul class="drop-zone-files" aria-live="polite"></ul>
          </div>

          Use positive decimal safe-integer counts; surrounding whitespace and leading zeros are allowed. Missing, blank, zero, negative, fractional, exponent, hexadecimal, nonnumeric or unsafe values impose no count limit. Each FileList entry counts, including repeated filenames. Add multiple to choose several files; the limit does not add it. A count-only input leaves accept as a picker hint.

          Supply the same direct polite [data-error] area. data-error-count localizes count feedback; empty uses a message naming the limit. Type errors take precedence, followed by size errors and then count. A count failure reports { input, files, rejected, reason: 'count' } through expressive:reject. Both arrays contain every chosen file, independently; there is no accepted first-N subset. The event is noncancelable. A changed effective count limit can report again after input/change; equivalent decimal values still deduplicate.

          Forms.validateField(input) silently refreshes all three constraints after script changes. Valid replacement, clearing, opt-out and native reset follow the same application-validity, cancellation, disabled-control, shadow and external-form recovery contract above. Always validate received counts, sizes and file contents on the server.

          Tokens

          Override these on the .drop-zone.

          Token Default
          --md-comp-drop-zone-shape 16px
          --md-comp-drop-zone-outline-color --md-sys-color-outline
          --md-comp-drop-zone-dragover-container-color --md-sys-color-primary-container
          --md-comp-drop-zone-dragover-color --md-sys-color-on-primary-container

          No matching pages

        • Source color

          The seed every generated ramp derives from. Pick one and browse the docs — the whole theme follows. Error does not: it is a fixed hue.

          Default source: Mist Iris.

          ExpressiveCSS presets: Lagoon, Harbor, Breeze, Cobalt, Iris.

          Bold

          Soft

          Mist

          Glow