diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc
index 45bb706..0c4489e 100644
--- a/core/includes/ajax.inc
+++ b/core/includes/ajax.inc
@@ -948,6 +948,38 @@ function ajax_command_remove($selector) {
 }
 
 /**
+ * Creates a Drupal Ajax 'redirect' command.
+ *
+ * The 'redirect' command instructs the browser to redirect to the give url.
+ *
+ * This command is implemented by Drupal.ajax.prototype.commands.redirect()
+ * defined in misc/ajax.js.
+ *
+ * @param $path
+ *   An internal drupal path.
+ * @param $options (optional)
+ *   An associative array of additional URL options to pass to url().
+ *
+ * @return
+ *   An array suitable for use with the ajax_render() function.
+ *
+ */
+function ajax_command_redirect($path, $options = array()) {
+  // Similar logic to drupal_goto.
+  $destination = drupal_container()->get('request')->query->get('destination');
+  if (!empty($destination) && !url_is_external($destination)) {
+    $parsed_destination = drupal_parse_url($destination);
+    $path = $parsed_destination['path'];
+    $options['query'] = $parsed_destination['query'];
+    $options['fragment'] = $parsed_destination['fragment'];
+  }
+  return array(
+    'command' => 'redirect',
+    'href' => url($path, $options),
+  );
+}
+
+/**
  * Creates a Drupal Ajax 'changed' command.
  *
  * This command instructs the client to mark each of the elements matched by the
@@ -1140,3 +1172,184 @@ function ajax_command_add_css($styles) {
     'data' => $styles,
   );
 }
+
+/**
+ * Wrapper around drupal_build_form to handle rendering forms in a jquery.ui
+ * dialog. Automatically handles a number of AJAX tasks.
+ *
+ * @param $form_id
+ *   The unique string identifying the desired form.
+ * @param $form_state
+ *   An array which stores information about the form.
+ * @param ...
+ *   Any additional arguments are passed on to drupal_build_form(), including
+ *   the unique form constructor function.
+ *
+ * @return
+ *   An array of ajax commands.
+ *
+ * @see drupal_get_form()
+ * @see drupal_build_form()
+ * @see ajax_deliver()
+ */
+function ajax_form_wrapper($form_id, &$form_state) {
+  // Build and render the form.
+  $form = drupal_build_form($form_id, $form_state);
+  $output = drupal_render($form);
+  // Some forms may have the title built in, if so set the title here (for non
+  // javascript users):
+  if (empty($form_state['ajax']) && !empty($form_state['title'])) {
+    drupal_set_title($form_state['title']);
+  }
+
+  if (!empty($form_state['ajax']) && (empty($form_state['executed']) || !empty($form_state['rerender']))) {
+    // If the form didn't execute and we're using ajax, build up a
+    // Ajax command list to execute.
+    $commands = array();
+
+    $display = '';
+    if ($messages = theme(array('status_messages', 'status_messages__ajax', 'status_messages__ajax__'. $form_id))) {
+      $display = $messages;
+    }
+    $display .= $output;
+
+    $title = empty($form_state['title']) ? drupal_get_title() : $form_state['title'];
+    // @todo document hook
+    drupal_alter(array('ajax_form_title', 'ajax_form_title_' . $form_id), $title, $form_state);
+
+    $url = empty($form_state['url']) ? url(current_path(), array('absolute' => TRUE, 'query' => drupal_container()->get('request')->query->all())) : $form_state['url'];
+
+    $commands[] = ajax_command_set_form($display, $title, $url);
+
+    // @todo document hook
+    drupal_alter(array('ajax_form_commands', 'ajax_form_commands_' . $form_id), $commands, $form_state);
+
+    return $commands;
+  }
+  elseif ($form_state['submitted'] && empty($form_state['rerender'])) {
+    if (!empty($form_state['ajax'])) {
+      // Form has submitted, dismiss it.
+      $commands = array();
+      $commands[] = ajax_command_dismiss_form();
+      $redirect = current_path();
+      dsm($form_state);
+      if (isset($form_state['redirect'])) {
+        if (is_array($form_state['redirect'])) {
+          $redirect = end($form_state['redirect']);
+        }
+        else {
+          $redirect = $form_state['redirect'];
+        }
+      }
+      $commands[] = ajax_command_redirect($redirect);
+      drupal_alter(array('ajax_form_commands', 'ajax_form_commands_' . $form_id), $commands, $form_state);
+      return $commands;
+    }
+    // Degrade to non-javascript behaviour.
+    drupal_redirect_form($form_state);
+  }
+
+  // Non javascript invocation - degrade to standard drupal_build_form
+  // behaviour. Forms can have the title built in, if so set the title here:
+  if (empty($form_state['ajax']) && !empty($form_state['title'])) {
+    drupal_set_title($form_state['title']);
+  }
+
+  return $output;
+}
+
+/**
+ * Creates a Drupal AJAX 'setForm' command.
+ *
+ * @param $output
+ *   The form to display in the modal.
+ * @param $title
+ *   The title.
+ * @param $url
+ *   An optional URL.
+ *
+ * @return
+ *   An array suitable for use with the ajax_render() function.
+ */
+function ajax_command_set_form($html, $title, $url = NULL) {
+  $command = array(
+    'command' => 'modalOpen',
+    'data' => $html,
+    'title' => $title,
+  );
+  if (isset($url)) {
+    $command['url'] = $url;
+  }
+  return $command;
+}
+
+/**
+ * Creates a Drupal AJAX 'dismissForm' command.
+ *
+ * @return
+ *   An array suitable for use with the ajax_render() function.
+ */
+function ajax_command_dismiss_form() {
+  $command = array(
+    'command' => 'modalClose',
+  );
+  return $command;
+}
+
+/**
+ * Form builder wrapper for displaying ajax forms in modals.
+ *
+ * Modules should use this as a menu callback to handle displaying forms in
+ * modals. Example hook_menu () item
+ *  @code
+ *   $items['node/%node/delete'] = array(
+ *    'title' => 'Delete',
+ *    'page callback' => 'ajax_modal_form',
+ *    'page arguments' => array('node_delete_confirm', 1),
+ *    'access callback' => 'node_access',
+ *    'access arguments' => array('delete', 1),
+ *    'weight' => 1,
+ *    'type' => MENU_LOCAL_TASK,
+ *    'context' => MENU_CONTEXT_INLINE,
+ *    'file' => 'node.pages.inc',
+ *   );
+ * @endcode
+ * In addition modules should ensure their links to ajax modals contain the
+ * 'modal' flag.
+ *
+ * @param $form_id
+ *   The unique string identifying the desired form.
+ * @param ...
+ *   Any additional arguments are passed on to drupal_build_form(), including
+ *   the unique form constructor function.
+ *
+ * @return mixed
+ *  If request Content-Type is ajax, returns array of ajax commands, otherwise
+ *  returns markup from drupal_get_form()
+ *
+ * @see l().
+*/
+function ajax_modal_form($form_id) {
+  // Get the form build arguments.
+  $args = func_get_args();
+  // Remove the $form_id.
+  array_splice($args, 0, 1);
+  // Check for the content-type.
+  $container = drupal_container();
+  $type = FALSE;
+  if ($container->has('request') && $container->has('content_negotiation')) {
+    $type = $container->get('content_negotiation')->getContentType($container->get('request'));
+  }
+  // Set sensible defaults if they don't already exist.
+  $form_state = array(
+    'rerender' => FALSE,
+    'ajax' => ($type == 'ajax'),
+    'no_redirect' => ($type == 'ajax'),
+    'build_info' => array(
+      'args' => $args,
+    )
+  );
+  $output = ajax_form_wrapper($form_id, $form_state);
+
+  return ($type  == 'ajax') ? array('#type' => 'ajax', '#commands' => $output) : $output;
+}
diff --git a/core/includes/common.inc b/core/includes/common.inc
index f1a7ce0..4a279ac 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2294,6 +2294,9 @@ function drupal_http_header_attributes(array $attributes = array()) {
  *     internal to the site, $options['language'] is used to determine whether
  *     the link is "active", or pointing to the current page (the language as
  *     well as the path must match). This element is also used by url().
+ *   - 'modal': Pass TRUE if the link should display the form in a modal, note
+ *     that the page callback should be ajax_modal_form() in order for this to
+ *     work.
  *   - Additional $options elements used by the url() function.
  *
  * @return string
@@ -2310,6 +2313,10 @@ function l($text, $path, array $options = array()) {
     'query' => array(),
     'html' => FALSE,
   );
+  if (!empty($options['modal'])) {
+    $options['attributes']['class'][] = 'use-ajax';
+    drupal_add_library('system', 'drupal.ajax.modal');
+  }
 
   // Append active class.
   // The link is only active, if its path corresponds to the current path, the
diff --git a/core/misc/ajax.js b/core/misc/ajax.js
index 58db308..e83db7c 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -40,6 +40,12 @@ Drupal.behaviors.AJAX = {
       }
     }
 
+    // Prevent ajax links to move page.
+    $('body').once('ajax-link').on('click', '.use-ajax', function (e) {
+      e.preventDefault();
+      e.stopPropagation();
+    });
+
     // Bind Ajax behaviors to all items showing the class.
     $('.use-ajax').once('ajax', function () {
       var element_settings = {};
@@ -547,6 +553,13 @@ Drupal.ajax.prototype.commands = {
   },
 
   /**
+   * Command to redirect a user.
+   */
+  redirect: function (ajax, response, status) {
+    window.location.href = response.href;
+  },
+
+  /**
    * Command to remove a chunk from the page.
    */
   remove: function (ajax, response, status) {
diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc
index 933a4ad..643eed6 100644
--- a/core/modules/image/image.admin.inc
+++ b/core/modules/image/image.admin.inc
@@ -37,6 +37,12 @@ function image_style_form($form, &$form_state, $style) {
   $title = t('Edit style %name', array('%name' => $style->label()));
   drupal_set_title($title, PASS_THROUGH);
 
+  // Make sure modals are prepared.
+  drupal_add_library('system', 'drupal.ajax.modal');
+
+  // Explicitly set the action for ajax behavior.
+  $form['#action'] = url('admin/config/media/image-styles/edit/' . $style->id());
+
   $form_state['image_style'] = $style;
   $form['#tree'] = TRUE;
   $form['#attached']['css'][drupal_get_path('module', 'image') . '/image.admin.css'] = array();
@@ -88,11 +94,13 @@ function image_style_form($form, &$form_state, $style) {
         $links['edit'] = array(
           'title' => t('edit'),
           'href' => 'admin/config/media/image-styles/edit/' . $style->id() . '/effects/' . $key,
+          'modal' => TRUE,
         );
       }
       $links['delete'] = array(
         'title' => t('delete'),
         'href' => 'admin/config/media/image-styles/edit/' . $style->id() . '/effects/' . $key . '/delete',
+        'modal' => TRUE,
       );
       $form['effects'][$key]['operations'] = array(
         '#type' => 'operations',
@@ -137,6 +145,9 @@ function image_style_form($form, &$form_state, $style) {
   $form['effects']['new']['add'] = array(
     '#type' => 'submit',
     '#value' => t('Add'),
+    '#ajax' => array(
+      'callback' => 'image_style_form_add_submit_modal'
+    ),
     '#validate' => array('image_style_form_add_validate'),
     '#submit' => array('image_style_form_submit', 'image_style_form_add_submit'),
   );
@@ -181,11 +192,44 @@ function image_style_form_add_submit($form, &$form_state) {
       'weight' => $form_state['values']['weight'],
     );
     image_effect_save($style, $effect);
+    $form_state['redirect'] = 'admin/config/media/image-styles/edit/' . $style->id();
     drupal_set_message(t('The image effect was successfully applied.'));
   }
 }
 
 /**
+ * Ajax modal handler for adding a new image effect to an image style.
+ */
+function image_style_form_add_submit_modal($form, &$form_state) {
+  $style = $form_state['image_style'];
+  // Check if this field has any configuration options.
+  $effect = image_effect_definition_load($form_state['values']['new']);
+
+  // Load the configuration form for this option.
+  if (isset($effect['form callback'])) {
+    // Set sensible defaults if they don't already exist.
+    $form_state = array(
+      'rerender' => FALSE,
+      'ajax' => TRUE,
+      'no_redirect' => TRUE,
+      'build_info' => array(
+        'args' => array(
+          $style,
+          $effect
+        )
+      )
+    );
+    // Return a modal.
+    return array('#type' => 'ajax', '#commands' => ajax_form_wrapper('image_effect_form', $form_state));
+  }
+  // If there's no form, immediately add the image effect.
+  else {
+    // Reload the page.
+    return array('#type' => 'ajax', '#commands' => array(ajax_command_redirect($form_state['redirect'])));
+  }
+}
+
+/**
  * Submit handler for saving an image style.
  */
 function image_style_form_submit($form, &$form_state) {
@@ -343,6 +387,13 @@ function image_effect_form($form, &$form_state, $style, $effect) {
   }
   $form_state['image_style'] = $style;
   $form_state['image_effect'] = $effect;
+  // Explicitly set the action for ajax behavior.
+  if (empty($effect['ieid'])) {
+    $form['#action'] = url('admin/config/media/image-styles/edit/' . $style->id() . '/add/' . $effect['name']);
+  }
+  else {
+    $form['#action'] = url('admin/config/media/image-styles/edit/' . $style->id() . '/effects/' . $effect['ieid']);
+  }
 
   if (!empty($effect['ieid'])) {
     $title = t('Edit %label effect', array('%label' => $effect['label']));
@@ -414,6 +465,9 @@ function image_effect_delete_form($form, &$form_state, $style, $effect) {
   $form_state['image_style'] = $style;
   $form_state['image_effect'] = $effect;
 
+  // Explicitly set the action for ajax behavior.
+  $form['#action'] = url('admin/config/media/image-styles/edit/' . $style->id() . '/effects/' . $effect['ieid']) . '/delete';
+
   $question = t('Are you sure you want to delete the @effect effect from the %style style?', array('%style' => $style->label(), '@effect' => $effect['label']));
   return confirm_form($form, $question, 'admin/config/media/image-styles/edit/' . $style->id(), '', t('Delete'));
 }
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index c763dc6..0318c5c 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -146,7 +146,7 @@ function image_menu() {
     'title' => 'Edit image effect',
     'description' => 'Edit an existing effect within a style.',
     'load arguments' => array(5, (string) IMAGE_STORAGE_EDITABLE),
-    'page callback' => 'drupal_get_form',
+    'page callback' => 'ajax_modal_form',
     'page arguments' => array('image_effect_form', 5, 7),
     'access arguments' => array('administer image styles'),
     'file' => 'image.admin.inc',
@@ -155,7 +155,7 @@ function image_menu() {
     'title' => 'Delete image effect',
     'description' => 'Delete an existing effect from a style.',
     'load arguments' => array(5, (string) IMAGE_STORAGE_EDITABLE),
-    'page callback' => 'drupal_get_form',
+    'page callback' => 'ajax_modal_form',
     'page arguments' => array('image_effect_delete_form', 5, 7),
     'access arguments' => array('administer image styles'),
     'file' => 'image.admin.inc',
@@ -164,7 +164,7 @@ function image_menu() {
     'title' => 'Add image effect',
     'description' => 'Add a new effect to a style.',
     'load arguments' => array(5),
-    'page callback' => 'drupal_get_form',
+    'page callback' => 'ajax_modal_form',
     'page arguments' => array('image_effect_form', 5, 7),
     'access arguments' => array('administer image styles'),
     'file' => 'image.admin.inc',
diff --git a/core/modules/node/node.admin.inc b/core/modules/node/node.admin.inc
index b35e839..da52694 100644
--- a/core/modules/node/node.admin.inc
+++ b/core/modules/node/node.admin.inc
@@ -557,6 +557,7 @@ function node_admin_nodes() {
       $operations['delete'] = array(
         'title' => t('delete'),
         'href' => 'node/' . $node->nid . '/delete',
+        'modal' => TRUE,
         'query' => $destination,
       );
     }
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 50395b1..e75c7bb 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1790,7 +1790,7 @@ function node_menu() {
   );
   $items['node/%node/delete'] = array(
     'title' => 'Delete',
-    'page callback' => 'drupal_get_form',
+    'page callback' => 'ajax_modal_form',
     'page arguments' => array('node_delete_confirm', 1),
     'access callback' => 'node_access',
     'access arguments' => array('delete', 1),
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 541f9bd..f7df57f 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1170,6 +1170,23 @@ function system_library_info() {
     ),
   );
 
+  // Drupal's Ajax framework, modal component.
+  $libraries['drupal.ajax.modal'] = array(
+    'title' => 'Drupal AJAX modals',
+    'website' => 'http://api.drupal.org/api/group/ajax/8',
+    'version' => VERSION,
+    'js' => array(
+      'core/misc/ajax.modal.js' => array('group' => JS_LIBRARY, 'weight' => 2),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'drupal'),
+      array('system', 'drupalSettings'),
+      array('system', 'drupal.ajax'),
+      array('system', 'jquery.ui.dialog')
+    ),
+  );
+
   // Drupal's batch API.
   $libraries['drupal.batch'] = array(
     'title' => 'Drupal batch API',
@@ -3370,6 +3387,11 @@ function confirm_form($form, $question, $path, $description = NULL, $yes = NULL,
     '#type' => 'link',
     '#title' => $no ? $no : t('Cancel'),
     '#href' => $options['path'],
+    '#attributes' => array(
+      'class' => array(
+        'modal-dismiss'
+      )
+    ),
     '#options' => $options,
   );
   // By default, render the form using theme_confirm_form().
