diff --git a/core/misc/icons/bebebe/dropzone-new.svg b/core/misc/icons/bebebe/dropzone-new.svg
new file mode 100644
index 0000000000..b3b8e494af
--- /dev/null
+++ b/core/misc/icons/bebebe/dropzone-new.svg
@@ -0,0 +1,13 @@
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="100" width="500" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+<g transform="translate(-95.714287,-390.93359)">
+<g fill="#7fc9f7" transform="matrix(1.840195,0,0,1.840195,331.1186,426.62055)">
+<path fill="#7fc9f7" d="m12.5,7h-5c-0.274,0-0.5-0.225-0.5-0.5v-5c0-0.275-0.225-0.5-0.5-0.5h-3c-0.275,0-0.5,0.225-0.5,0.5v12.03c0,0.275,0.225,0.5,0.5,0.5h9.002c0.275,0,0.5-0.225,0.5-0.5v-6.03c0-0.275-0.225-0.5-0.5-0.5zm-4-1h4c0.275,0,0.34-0.159,0.146-0.354l-4.295-4.292c-0.195-0.195-0.353-0.129-0.353,0.146v4c0,0.275,0.225,0.5,0.5,0.5z"/>
+</g>
+<rect stroke-linejoin="bevel" stroke-dasharray="9.26, 9.26" stroke-dashoffset="30.87" ry="4.464" height="63.86" width="66.36" stroke="#bebebe" stroke-linecap="round" stroke-miterlimit="4" y="408.9" x="110.7" stroke-width="3.087" fill="none"/>
+<rect stroke-linejoin="bevel" stroke-dasharray="9.26, 9.26" stroke-dashoffset="30.87" ry="4.464" height="63.86" width="66.36" stroke="#6dcbff" stroke-linecap="round" stroke-miterlimit="4" y="408.9" x="210.7" stroke-width="3.087" fill="none"/>
+<path fill="#6dcbff" d="m241.7,428.8,0,9.031-9.031,0,0,6.031,9.031,0,0,9,6,0,0-9,9.031,0,0-6.031-9.031,0,0-9.031-6,0z"/>
+<g fill="#bfbfbd" stroke-opacity="0" transform="matrix(1.840195,0,0,1.840195,131.1186,426.62055)">
+<path fill="#bebebe" stroke-opacity="0" d="m12.5,7h-5c-0.274,0-0.5-0.225-0.5-0.5v-5c0-0.275-0.225-0.5-0.5-0.5h-3c-0.275,0-0.5,0.225-0.5,0.5v12.03c0,0.275,0.225,0.5,0.5,0.5h9.002c0.275,0,0.5-0.225,0.5-0.5v-6.03c0-0.275-0.225-0.5-0.5-0.5zm-4-1h4c0.275,0,0.34-0.159,0.146-0.354l-4.295-4.292c-0.195-0.195-0.353-0.129-0.353,0.146v4c0,0.275,0.225,0.5,0.5,0.5z"/>
+</g>
+</g>
+</svg>
diff --git a/core/misc/icons/ee0000/ex.svg b/core/misc/icons/ee0000/ex.svg
new file mode 100644
index 0000000000..6b45a1d572
--- /dev/null
+++ b/core/misc/icons/ee0000/ex.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#ee0000" d="M3.51 13.925c.194.194.512.195.706.001l3.432-3.431c.194-.194.514-.194.708 0l3.432 3.431c.192.194.514.193.707-.001l1.405-1.417c.191-.195.189-.514-.002-.709l-3.397-3.4c-.192-.193-.192-.514-.002-.708l3.401-3.43c.189-.195.189-.515 0-.709l-1.407-1.418c-.195-.195-.513-.195-.707-.001l-3.43 3.431c-.195.194-.516.194-.708 0l-3.432-3.431c-.195-.195-.512-.194-.706.001l-1.407 1.417c-.194.195-.194.515 0 .71l3.403 3.429c.193.195.193.514-.001.708l-3.4 3.399c-.194.195-.195.516-.001.709l1.406 1.419z"/></svg>
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index be1e136958..7190139d7d 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -1414,6 +1414,8 @@ function template_preprocess_file_link(&$variables) {
   // Use the description as the link text if available.
   if (empty($variables['description'])) {
     $link_text = $file_entity->getFilename();
+    $link_text = file_shorten_filename($link_text);
+    $options['attributes']['title'] = $file_entity->getFilename();
   }
   else {
     $link_text = $variables['description'];
@@ -1436,6 +1438,31 @@ function template_preprocess_file_link(&$variables) {
   $variables['link'] = \Drupal::l($link_text, Url::fromUri($url, $options));
 }
 
+/**
+ * Shortens a file name.
+ *
+ * @param string $filename
+ *   The filename to shorten.
+ * @param int $limit
+ *   (optional) The maximum length of the filename (without the extension) that
+ *   will not be shortened.
+ *
+ * @return string
+ *   The shortened filename, or the original filename if it's less than $limit
+ *   characters long.
+ */
+function file_shorten_filename($filename, $limit = 10) {
+  $ext = pathinfo($filename, PATHINFO_EXTENSION);
+
+  $name = pathinfo($filename, PATHINFO_FILENAME);
+  if (strlen($name) > $limit) {
+    return substr($name, 0, $limit) . '...' . substr($name, -4) . '.' . $ext;
+  }
+  else {
+    return $filename;
+  }
+}
+
 /**
  * Gets a class for the icon for a MIME type.
  *
diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php
index ca4e887a1b..dea367cc16 100644
--- a/core/modules/file/src/Element/ManagedFile.php
+++ b/core/modules/file/src/Element/ManagedFile.php
@@ -182,10 +182,11 @@ public static function uploadAjaxCallback(&$form, FormStateInterface &$form_stat
     $current_file_count = $form_state->get('file_upload_delta_initial');
     if (isset($form['#file_upload_delta']) && $current_file_count < $form['#file_upload_delta']) {
       $form[$current_file_count]['#attributes']['class'][] = 'ajax-new-content';
+      $form[$current_file_count]['#attributes']['class'][] = 'managed-file';
     }
     // Otherwise just add the new content class on a placeholder.
     else {
-      $form['#suffix'] .= '<span class="ajax-new-content"></span>';
+      $form['#suffix'] .= '<span class="ajax-new-content managed-file"></span>';
     }
 
     $status_messages = ['#type' => 'status_messages'];
@@ -239,7 +240,7 @@ public static function processManagedFile(&$element, FormStateInterface $form_st
       '#name' => $parents_prefix . '_upload_button',
       '#type' => 'submit',
       '#value' => t('Upload'),
-      '#attributes' => ['class' => ['js-hide']],
+      '#attributes' => ['class' => ['upload-button', 'js-hide']],
       '#validate' => [],
       '#submit' => ['file_managed_file_submit'],
       '#limit_validation_errors' => [$element['#parents']],
@@ -256,6 +257,7 @@ public static function processManagedFile(&$element, FormStateInterface $form_st
       '#name' => $parents_prefix . '_remove_button',
       '#type' => 'submit',
       '#value' => $element['#multiple'] ? t('Remove selected') : t('Remove'),
+      '#attributes' => ['class' => ['remove-button']],
       '#validate' => [],
       '#submit' => ['file_managed_file_submit'],
       '#limit_validation_errors' => [$element['#parents']],
diff --git a/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php b/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php
index 7b7ce1aff6..839ca1db9d 100644
--- a/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php
+++ b/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php
@@ -266,7 +266,7 @@ public static function process($element, FormStateInterface $form_state, $form)
       '#description' => t('This text will be used by screen readers, search engines, or when the image cannot be loaded.'),
       // @see https://www.drupal.org/node/465106#alt-text
       '#maxlength' => 512,
-      '#weight' => -12,
+      '#weight' => 1,
       '#access' => (bool) $item['fids'] && $element['#alt_field'],
       '#required' => $element['#alt_field_required'],
       '#element_validate' => $element['#alt_field_required'] == 1 ? [[get_called_class(), 'validateRequiredFields']] : [],
@@ -277,7 +277,7 @@ public static function process($element, FormStateInterface $form_state, $form)
       '#default_value' => isset($item['title']) ? $item['title'] : '',
       '#description' => t('The title is used as a tool tip when the user hovers the mouse over the image.'),
       '#maxlength' => 1024,
-      '#weight' => -11,
+      '#weight' => 2,
       '#access' => (bool) $item['fids'] && $element['#title_field'],
       '#required' => $element['#title_field_required'],
       '#element_validate' => $element['#title_field_required'] == 1 ? [[get_called_class(), 'validateRequiredFields']] : [],
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseFilledTest.php b/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseFilledTest.php
index 7573fc4a92..54217c5c25 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseFilledTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatePathRC1TestBaseFilledTest.php
@@ -112,7 +112,8 @@ public function testUpdatedSite() {
     $this->assertRaw('+31612345679');
     $this->assertText('Test Article - New title');
     $this->assertText('test.txt');
-    $this->assertText('druplicon.small');
+    // The full-length file name is druplicon.small_.png.
+    $this->assertText('druplicon....all_.png');
     $this->assertRaw('General discussion');
     $this->assertText('Test Article - New title');
     $this->assertText('Test 1');
diff --git a/core/modules/system/tests/src/Functional/Update/UpdatePathTestBaseFilledTest.php b/core/modules/system/tests/src/Functional/Update/UpdatePathTestBaseFilledTest.php
index 2ed58fbabc..4636616d32 100644
--- a/core/modules/system/tests/src/Functional/Update/UpdatePathTestBaseFilledTest.php
+++ b/core/modules/system/tests/src/Functional/Update/UpdatePathTestBaseFilledTest.php
@@ -113,7 +113,8 @@ public function testUpdatedSite() {
     $this->assertRaw('+31612345679');
     $this->assertText('Test Article - New title');
     $this->assertText('test.txt');
-    $this->assertText('druplicon.small');
+    // The full-length file name is druplicon.small_.png.
+    $this->assertText('druplicon....all_.png');
     $this->assertRaw('General discussion');
     $this->assertText('Test Article - New title');
     $this->assertText('Test 1');
diff --git a/core/themes/seven/css/components/dropzone.css b/core/themes/seven/css/components/dropzone.css
new file mode 100644
index 0000000000..734f7869b1
--- /dev/null
+++ b/core/themes/seven/css/components/dropzone.css
@@ -0,0 +1,346 @@
+/**
+ * Field Widgets.
+ */
+.field--widget-image-image,
+.field--widget-file-generic,
+.field--widget-image-image td,
+.field--widget-file-generic td {
+  position: relative;
+}
+
+/**
+ * Remove Button
+ */
+.field--widget-image-image .remove-button,
+.field--widget-file-generic .remove-button {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  text-align: right;
+  line-height: 17px;
+  text-transform: lowercase;
+  min-width: 95px;
+  font-weight: normal;
+  background: url(../../../../misc/icons/787878/ex.svg) right 0 no-repeat;
+  border: 0;
+  margin: 0;
+  padding: 0 1.5em;
+  float: right;
+  width: auto;
+  text-shadow: 0;
+}
+
+[dir="rtl"] .field--widget-image-image .remove-button,
+[dir="rtl"] .field--widget-file-generic .remove-button {
+  margin: 0 0 0 10px;
+  float: left;
+  text-align: left;
+  background-position: left 0;
+}
+
+.field--widget-image-image .remove-button:focus,
+.field--widget-file-generic .remove-button:focus {
+  box-shadow: none;
+  outline: none;
+}
+
+.field--widget-image-image .remove-button:hover,
+.field--widget-file-generic .remove-button:hover {
+  background: url(../../../../misc/icons/ee0000/ex.svg) right 0 no-repeat;
+  border: 0;
+  box-shadow: none;
+  color: #ee0000;
+}
+
+[dir="rtl"] .field--widget-image-image .remove-button:hover,
+[dir="rtl"] .field--widget-file-generic .remove-button:hover {
+  background-position: left 0;
+}
+
+/**
+ * Remove Buttons Ajax Throbber
+ */
+.field--widget-image-image .remove-button + .ajax-progress,
+.field--widget-file-generic .remove-button + .ajax-progress {
+  position: absolute;
+  right: 85px;
+  top: 8px;
+}
+
+[dir="rtl"] .field--widget-image-image .remove-button + .ajax-progress,
+[dir="rtl"] .field--widget-file-generic .remove-button + .ajax-progress {
+  right: auto;
+  left: 85px;
+}
+
+/**
+ * Upload Buttons Ajax Throbber
+ */
+.field--widget-image-image .upload-button + .ajax-progress,
+.field--widget-file-generic .upload-button + .ajax-progress {
+  position: absolute;
+  left: 50px;
+  top: 50%;
+  margin: -10px 0 0 -13px;
+}
+
+[dir="rtl"] .field--widget-image-image .upload-button + .ajax-progress,
+[dir="rtl"] .field--widget-file-generic .upload-button + .ajax-progress {
+  left: auto;
+  right: 50px;
+  margin: -10px -13px 0 0;
+}
+
+/**
+ * Image Widget
+ */
+.field--widget-image-image .dropzone-trigger {
+  display: none;
+}
+
+.field--widget-image-image .image-widget-data .form-item {
+  width: 100%;
+  float: left;
+}
+
+.field--widget-image-image .image-preview img {
+  border: 0;
+  max-width: 100px;
+  height: auto;
+  padding: 0 10px 0 0;
+  float: left;
+}
+
+[dir="rtl"] .field--widget-image-image .image-preview img {
+  float: right;
+  margin: 0 0 0 10px;
+}
+
+/**
+ * Multi-Image Uploads Table
+ */
+.dropzone-enabled table {
+  margin: 10px 0;
+}
+.dropzone-enabled table tr td:nth-child(2),
+.dropzone-enabled table tr td:last-child {
+  vertical-align: top;
+}
+.dropzone-enabled table tr th:last-child,
+.dropzone-enabled table tr td:last-child {
+  text-align: right;
+}
+.field--widget-file-generic td .form-managed-file .form-item {
+  padding: 10px 0 0 0;
+}
+@media screen and (min-width: 414px) {
+  .field--widget-image-image td .image-widget-data .form-item {
+    padding-left: 27px;
+  }
+
+  .field--widget-file-generic td .form-managed-file .form-item {
+    padding: 10px 0 0 27px;
+  }
+}
+
+.dropzone-enabled table tr th:nth-child(2),
+.dropzone-enabled table tr td:nth-child(2),
+.dropzone-enabled table tr th:last-child,
+.dropzone-enabled table tr td:last-child {
+  width: 30%;
+  white-space: nowrap;
+}
+
+.field--type-image .details-wrapper,
+.field--type-file .details-wrapper {
+  overflow: auto;
+}
+
+.dropzone-enabled table {
+  margin: 10px 0;
+  width: auto;
+}
+
+@media screen and (min-width: 414px) {
+  .dropzone-enabled table tr th:last-child,
+  .dropzone-enabled table tr td:last-child,
+  .dropzone-enabled table tr th:nth-child(2),
+  .dropzone-enabled table tr td:nth-child(2) {
+    width: 30%;
+    white-space: nowrap;
+  }
+  .dropzone-enabled table {
+    table-layout: fixed;
+  }
+}
+
+@media screen and (min-width: 767px) {
+  .dropzone-enabled table tr th:last-child,
+  .dropzone-enabled table tr td:last-child,
+  .dropzone-enabled table tr th:nth-child(2),
+  .dropzone-enabled table tr td:nth-child(2) {
+    width: 25%;
+    white-space: nowrap;
+  }
+}
+
+
+
+
+.field--widget-image-image td .image-widget-data {
+  float: none;
+}
+
+.field--widget-image-image td .image-preview img {
+  padding-bottom: 10px;
+}
+
+.field--widget-image-image td .image-widget-data .form-item {
+  margin-top: 1em;
+}
+
+.field--widget-file-generic td .form-managed-file .form-item {
+  padding: 0 0 0 27px;
+}
+
+[dir="rtl"] .field--widget-file-generic td .form-managed-file .form-item {
+  padding: 0 27px 0 0;
+}
+
+/**
+ * Dropzone.
+ */
+.dropzone-wrapper {
+  width: 100%;
+  border: 1px solid #bfbfbf;
+  border-radius: 2px;
+  background: #fcfcfa;
+  box-sizing: border-box;
+}
+.dropzone {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  min-height: 100px;
+}
+.dropzone.empty {
+  display: table;
+}
+.dropzone.empty > div {
+  display: table-cell;
+  vertical-align: top;
+  box-sizing: border-box;
+}
+span.ajax-new-content.managed-file {
+  display: block;
+}
+.dropzone .has-file,
+.dropzone .form-managed-file {
+  padding: 10px;
+}
+.dropzone.empty .form-managed-file {
+  padding: 0;
+}
+
+/* hide error message */
+.dropzone .messages--error { display: none; }
+
+/* form file */
+.dropzone .js-form-file {
+  position: absolute;
+  left: 0;
+  top: 0;
+  cursor: pointer;
+  min-height: 100px;
+  height: 100%;
+  width: 100px;
+  display: inline-block;
+  opacity: 0;
+}
+
+[dir="rtl"] .dropzone .js-form-file {
+  right: 0;
+  left: auto;
+}
+
+.dropzone .file {
+  background: none;
+  padding: 0;
+}
+
+.dropzone .dropzone-trigger {
+  position: relative;
+  text-align: center;
+  min-height: 100px;
+  height: 100%;
+  width: 100px;
+  border-right: 1px solid #bfbfbf;
+  background: url('../../../../misc/icons/bebebe/dropzone-new.svg') -200px center no-repeat;
+}
+
+[dir="rtl"] .dropzone .dropzone-trigger {
+  border-right: none;
+  border-left: 1px solid #bfbfbf;
+}
+
+.dropzone.empty .dropzone-trigger {
+  display: table-cell;
+  background-position: 0 center;
+}
+
+
+.dropzone .dropzone-trigger.is-hovering {
+  background-position: -100px center;
+}
+
+.dropzone .dropzone-trigger.is-complete,
+.field--type-image .dropzone .dropzone-trigger.is-complete,
+.field--type-file .dropzone .dropzone-trigger.is-complete {
+  background-position: -200px center;
+}
+
+
+.dropzone.empty .dropzone-trigger.is-complete {
+  background-position: 0 center;
+}
+
+.dropzone-description {
+  padding: 10px;
+  -moz-box-sizing: border-box;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  display: table-cell;
+}
+
+.dropzone-preview {
+  border: 1px solid #ccc;
+  border-radius: 2px;
+  background: #fdfdfc;
+  margin-bottom: 1em;  padding: 0.5em 1em;
+}
+
+.dropzone-buttons {
+  padding: 0;
+  margin: 0 10px 10px 0;
+  list-style: none;
+}
+
+[dir="rtl"] .dropzone-buttons {
+  margin: 0 0 10px 10px;
+}
+
+.dropzone-buttons li {
+  display: inline-block;
+  margin: 0 10px 0 0;
+}
+
+[dir="rtl"] .dropzone-buttons li {
+  margin: 0 0 0 10px;
+}
+
+.dropzone .dropzone-trigger.is-hidden,
+.field--type-image .dropzone .dropzone-trigger.is-hidden,
+.field--type-file .dropzone .dropzone-trigger.is-hidden {
+  background-image: none;
+}
+
diff --git a/core/themes/seven/js/dropzone.js b/core/themes/seven/js/dropzone.js
new file mode 100644
index 0000000000..66871a5c7e
--- /dev/null
+++ b/core/themes/seven/js/dropzone.js
@@ -0,0 +1,124 @@
+(function ($, Drupal) {
+
+  'use strict';
+
+  Drupal.behaviors.dropzone = {
+    attach: function (context) {
+
+      var formFile = $(context).find('.js-form-file');
+      var detailsWrapper = $(context).find('.dropzone').parents('details');
+
+      formFile.on('dragover mouseenter', function () {
+        $(this).closest('.dropzone').find('.dropzone-trigger').addClass('is-hovering');
+      });
+      formFile.on('dragleave mouseleave', function () {
+        $(this).closest('.dropzone').find('.dropzone-trigger').removeClass('is-hovering');
+      });
+
+      // Add a class to details wrapper so we can style the table with more control
+      if (detailsWrapper.length) {
+        $('.dropzone').parents('details').addClass('dropzone-enabled');
+      }
+
+      /**
+       * Fix the reflowing of the changed marker. Output after filesize span.
+       */
+      if (Drupal.tableDrag) {
+        Drupal.tableDrag.prototype.row.prototype.markChanged = function () {
+          var marker = Drupal.theme('tableDragChangedMarker');
+          var cell = $(this.element).find('td:first-of-type');
+          if (cell.find('abbr.tabledrag-changed').length === 0) {
+            if (cell.find('.file-size').length) {
+              cell.find('.file-size').append(marker);
+            }
+            else {
+              cell.append(marker);
+            }
+          }
+        };
+      }
+
+
+      Drupal.ajax.instances
+        .filter(function (instance) {
+          if (instance && instance.element) {
+            var element = $(instance.element);
+            return element.length > 0 && element.hasClass('upload-button') && element.once('magic').length > 0;
+          }
+          else {
+            return null;
+          }
+        })
+        .forEach(function (instance) {
+          var element = $(instance.element);
+          element.closest('.dropzone').find('.js-upload-image').on('click', function () {
+            $(this).closest('.js-form-item').find('input[type="file"]').trigger('click');
+          });
+
+          var beforeSend = instance.beforeSend;
+          element.bind('beforeSend', beforeSend);
+
+          instance.beforeSend = function (xmlhttprequest, options) {
+            beforeSend.call(this, xmlhttprequest, options);
+            element.trigger('beforeSend');
+            element.closest('.dropzone').find('.dropzone-trigger').addClass('is-hidden');
+          };
+
+
+          instance.success = function (response, status) {
+            $(this.element).prop('disabled', false);
+
+            // Save element's ancestors tree so if the element is removed from the dom
+            // we can try to refocus one of its parents. Using addBack reverse the
+            // result array, meaning that index 0 is the highest parent in the hierarchy
+            // in this situation it is usually a <form> element.
+            var elementParents = $(this.element).parents('[data-drupal-selector]').addBack().toArray();
+
+            // Track if any command is altering the focus so we can avoid changing the
+            // focus set by the Ajax command.
+            var focusChanged = false;
+            for (var i in response) {
+              if (response.hasOwnProperty(i) && response[i].command && this.commands[response[i].command]) {
+                this.commands[response[i].command](this, response[i], status);
+                if (response[i].command === 'invoke' && response[i].method === 'focus') {
+                  focusChanged = true;
+                }
+              }
+            }
+
+            // If the focus hasn't be changed by the ajax commands, try to refocus the
+            // triggering element or one of its parents if that element does not exist
+            // anymore.
+            var target;
+            if (!focusChanged && this.element && !$(this.element).data('disable-refocus')) {
+              target = false;
+              for (var n = elementParents.length - 1; !target && n > 0; n--) {
+                target = document.querySelector('[data-drupal-selector="' + elementParents[n].getAttribute('data-drupal-selector') + '"]');
+              }
+              if (target) {
+                $(target).trigger('focus');
+              }
+            }
+
+            if (target && $(this.element).hasClass('upload-button')) {
+              $(target).find('.dropzone-trigger').addClass('is-complete').next('.js-form-managed-file').addClass('has-file');
+            }
+
+            // Reattach behaviors, if they were detached in beforeSerialize(). The
+            // attachBehaviors() called on the new content from processing the response
+            // commands is not sufficient, because behaviors from the entire form need
+            // to be reattached.
+            if (this.$form) {
+              var settings = this.settings || drupalSettings;
+              Drupal.attachBehaviors(this.$form.get(0), settings);
+            }
+
+            // Remove any response-specific settings so they don't get used on the next
+            // call by mistake.
+            this.settings = null;
+          };
+        });
+    }
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/seven/seven.libraries.yml b/core/themes/seven/seven.libraries.yml
index 508c0fe497..abbc6d3202 100644
--- a/core/themes/seven/seven.libraries.yml
+++ b/core/themes/seven/seven.libraries.yml
@@ -14,6 +14,7 @@ global-styling:
       css/components/buttons.css: {}
       css/components/messages.css: {}
       css/components/dropbutton.component.css: {}
+      css/components/dropzone.css: {}
       css/components/entity-meta.css: {}
       css/components/field-ui.css: {}
       css/components/form.css: {}
@@ -38,7 +39,11 @@ global-styling:
       css/theme/colors.css: {}
     layout:
       css/layout/layout.css: {}
+    js:
+      js/dropzone.js: {}
   dependencies:
+    - core/drupal.ajax
+    - core/drupal.progress
     - system/admin
 
 node-form:
diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme
index 2ad947c80b..68f8227b0b 100644
--- a/core/themes/seven/seven.theme
+++ b/core/themes/seven/seven.theme
@@ -163,6 +163,13 @@ function seven_form_node_form_alter(&$form, FormStateInterface $form_state) {
   $form['revision_information']['#group'] = 'meta';
 }
 
+/**
+ * Implements hook_theme_suggestions_form_element_alter().
+ */
+function seven_theme_suggestions_form_element_alter(&$suggestions, $variables) {
+  $suggestions[] = 'form_element__' . $variables['element']['#type'];
+}
+
 /**
  * Implements hook_form_BASE_FORM_ID_alter() for \Drupal\media\MediaForm.
  */
diff --git a/core/themes/seven/templates/form-element--managed-file.html.twig b/core/themes/seven/templates/form-element--managed-file.html.twig
new file mode 100644
index 0000000000..9ab918eba7
--- /dev/null
+++ b/core/themes/seven/templates/form-element--managed-file.html.twig
@@ -0,0 +1,105 @@
+{#
+/**
+ * @file
+ * Theme override for a managed file form element.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing element.
+ * - errors: (optional) Any errors for this form element, may not be set.
+ * - prefix: (optional) The form element prefix, may not be set.
+ * - suffix: (optional) The form element suffix, may not be set.
+ * - required: The required marker, or empty if the associated form element is
+ *   not required.
+ * - type: The type of the element.
+ * - name: The name of the element.
+ * - label: A rendered label element.
+ * - label_display: Label display setting. It can have these values:
+ *   - before: The label is output before the element. This is the default.
+ *     The label includes the #title and the required marker, if #required.
+ *   - after: The label is output after the element. For example, this is used
+ *     for radio and checkbox #type elements. If the #title is empty but the
+ *     field is #required, the label will contain only the required marker.
+ *   - invisible: Labels are critical for screen readers to enable them to
+ *     properly navigate through forms but can be visually distracting. This
+ *     property hides the label for everyone except screen readers.
+ *   - attribute: Set the title attribute on the element to create a tooltip but
+ *     output no label element. This is supported only for checkboxes and radios
+ *     in \Drupal\Core\Render\Element\CompositeFormElementTrait::preRenderCompositeFormElement().
+ *     It is used where a visual label is not needed, such as a table of
+ *     checkboxes where the row and column provide the context. The tooltip will
+ *     include the title and required marker.
+ * - description: (optional) A list of description properties containing:
+ *    - content: A description of the form element, may not be set.
+ *    - attributes: (optional) A list of HTML attributes to apply to the
+ *      description content wrapper. Will only be set when description is set.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element. This is the default
+ *     value.
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
+ * - disabled: True if the element is disabled.
+ * - title_display: Title display setting.
+ *
+ * @see template_preprocess_form_element()
+ */
+#}
+{%
+set classes = [
+'js-form-item',
+'form-item',
+'js-form-type-' ~ type|clean_class,
+'form-type-' ~ type|clean_class,
+'js-form-item-' ~ name|clean_class,
+'form-item-' ~ name|clean_class,
+'dropzone-wrapper',
+'clearfix',
+disabled == 'disabled' ? 'form-disabled',
+errors ? 'form-item--error',
+]
+%}
+{%
+set description_classes = [
+'dropzone-description',
+description_display == 'invisible' ? 'visually-hidden',
+]
+%}
+
+<div{{ attributes.addClass(classes) }}>
+  {% if prefix is not empty %}
+    <span class="field-prefix">{{ prefix }}</span>
+  {% endif %}
+  <div class="dropzone js-dropzone{% if element['#value']['fids'] is empty %} empty{% endif %}">
+    <div class="dropzone-trigger"></div>
+
+    {{ children }}
+
+    {% if suffix is not empty %}
+      <span class="field-suffix">{{ suffix }}</span>
+    {% endif %}
+    {% if label_display == 'after' %}
+      {{ label }}
+    {% endif %}
+
+    {% if description.content %}
+      <div{{ description.attributes.addClass(description_classes) }}>
+        <ul class="dropzone-buttons">
+          <li>
+            <span class="button button--small js-upload-image">
+              {% set count = element['#multiple'] ? 1 : 2 %}
+              {% trans %}
+              Add Files
+              {% plural count %}
+              Add File
+              {% endtrans %}
+            </span>
+          </li>
+          {#<li><span href="#" class="button button--small js-browse-library">{{ 'Browse Library'|t }}</span></li>#}
+        </ul>
+        {{ description.content }}
+      </div>
+    {% endif %}
+
+  </div>
+
+</div>
