diff --git a/core/modules/overlay/overlay-parent.js b/core/modules/overlay/overlay-parent.js
index d1fc4ca..24eb105 100644
--- a/core/modules/overlay/overlay-parent.js
+++ b/core/modules/overlay/overlay-parent.js
@@ -2,7 +2,7 @@
 (function ($) {
 
 /**
- * Open the overlay, or load content into it, when an admin link is clicked.
+ * Open the overlay, or load content into it, when an overlay link is clicked.
  */
 Drupal.behaviors.overlayParent = {
   attach: function (context, settings) {
@@ -337,33 +337,33 @@ Drupal.overlay.setFocusBefore = function ($element, document) {
 };
 
 /**
- * Check if the given link is in the administrative section of the site.
+ * Check if the given link should be displayed in the overlay.
  *
  * @param url
  *   The url to be tested.
  *
  * @return boolean
- *   TRUE if the URL represents an administrative link, FALSE otherwise.
+ *   TRUE if the URL represents an overlay link, FALSE otherwise.
  */
-Drupal.overlay.isAdminLink = function (url) {
+Drupal.overlay.isOverlayLink = function (url) {
   if (Drupal.overlay.isExternalLink(url)) {
     return false;
   }
 
   var path = this.getPath(url);
 
-  // Turn the list of administrative paths into a regular expression.
-  if (!this.adminPathRegExp) {
+  // Turn the list of overlay paths into a regular expression.
+  if (!this.overlayPathRegExp) {
     var regExpPrefix = '^' + Drupal.settings.pathPrefix + '(';
-    var adminPaths = regExpPrefix + Drupal.settings.overlay.paths.admin.replace(/\s+/g, ')$|' + regExpPrefix) + ')$';
-    var nonAdminPaths = regExpPrefix + Drupal.settings.overlay.paths.non_admin.replace(/\s+/g, ')$|'+ regExpPrefix) + ')$';
-    adminPaths = adminPaths.replace(/\*/g, '.*');
-    nonAdminPaths = nonAdminPaths.replace(/\*/g, '.*');
-    this.adminPathRegExp = new RegExp(adminPaths);
-    this.nonAdminPathRegExp = new RegExp(nonAdminPaths);
+    var overlayPaths = regExpPrefix + Drupal.settings.overlay.paths.overlay.replace(/\s+/g, ')$|' + regExpPrefix) + ')$';
+    var nonOverlayPaths = regExpPrefix + Drupal.settings.overlay.paths.non_overlay.replace(/\s+/g, ')$|'+ regExpPrefix) + ')$';
+    overlayPaths = overlayPaths.replace(/\*/g, '.*');
+    nonOverlayPaths = nonOverlayPaths.replace(/\*/g, '.*');
+    this.overlayPathRegExp = new RegExp(overlayPaths);
+    this.nonOverlayPathRegExp = new RegExp(nonOverlayPaths);
   }
 
-  return this.adminPathRegExp.exec(path) && !this.nonAdminPathRegExp.exec(path);
+  return this.overlayPathRegExp.exec(path) && !this.nonOverlayPathRegExp.exec(path);
 };
 
 /**
@@ -512,8 +512,7 @@ Drupal.overlay.eventhandlerRestoreDisplacedElements = function (event) {
 };
 
 /**
- * Event handler: overrides href of administrative links to be opened in
- * the overlay.
+ * Event handler: overrides href of overlay links.
  *
  * This click event handler should be bound to any document (for example the
  * overlay iframe) of which you want links to open in the overlay.
@@ -563,8 +562,7 @@ Drupal.overlay.eventhandlerOverrideLink = function (event) {
     if (anchor.length == 0 || anchor.charAt(0) == '#') {
       return;
     }
-    // Open admin links in the overlay.
-    else if (this.isAdminLink(href)) {
+    else if (this.isOverlayLink(href)) {
       // If the link contains the overlay-restore class and the overlay-context
       // state is set, also update the parent window's location.
       var parentLocation = ($target.hasClass('overlay-restore') && typeof $.bbq.getState('overlay-context') == 'string')
@@ -589,7 +587,7 @@ Drupal.overlay.eventhandlerOverrideLink = function (event) {
           .attr('href', href);
       }
     }
-    // Non-admin links should close the overlay and open in the main window,
+    // Non-overlay links should close the overlay and open in the main window,
     // which is the default action for a link. We only need to handle them
     // if the overlay is open and the clicked link is inside the overlay iframe.
     else if (this.isOpen && target.ownerDocument === this.iframeWindow.document) {
@@ -606,10 +604,9 @@ Drupal.overlay.eventhandlerOverrideLink = function (event) {
         $target.attr('href', $.param.fragment(href, { 'overlay-context': this.getPath(window.location) + window.location.search }));
 
         // When the link has a destination query parameter and that destination
-        // is an admin link we need to fragmentize it. This will make it reopen
-        // in the overlay.
+        // is an overlay link we need to fragmentize it.
         var params = $.deparam.querystring(href);
-        if (params.destination && this.isAdminLink(params.destination)) {
+        if (params.destination && this.isOverlayLink(params.destination)) {
           var fragmentizedDestination = $.param.fragment(this.getPath(window.location), { overlay: params.destination });
           $target.attr('href', $.param.querystring(href, { destination: fragmentizedDestination }));
         }
@@ -723,7 +720,7 @@ Drupal.overlay.eventhandlerDispatchEvent = function (event) {
 };
 
 /**
- * Make a regular admin link into a URL that will trigger the overlay to open.
+ * Make a regular link into a URL that will trigger the overlay to open.
  *
  * @param link
  *   A JavaScript Link object (i.e. an <a> element).
diff --git a/core/modules/overlay/overlay.api.php b/core/modules/overlay/overlay.api.php
index bc23546..6a9e077 100644
--- a/core/modules/overlay/overlay.api.php
+++ b/core/modules/overlay/overlay.api.php
@@ -36,5 +36,48 @@ function hook_overlay_child_initialize() {
 }
 
 /**
+ * Define overlay paths.
+ *
+ * Modules may specify whether or not the paths they define in hook_menu()
+ * should open in the overlay.
+ *
+ * To change the status of menu items defined in another module's hook_menu(),
+ * modules should implement hook_overlay_paths_alter().
+ *
+ * @return
+ *   An associative array. For each item, the key is the path in question, in
+ *   a format acceptable to drupal_match_path(). The value for each item should
+ *   be TRUE (for paths that should be displayed in the overlay) or FALSE (for
+ *   paths that should load in a regular browser window).
+ *
+ * @see hook_menu()
+ * @see drupal_match_path()
+ * @see hook_overlay_paths_alter()
+ */
+function hook_overlay_paths() {
+  $paths = array(
+    'mymodule/*/add' => TRUE,
+    'mymodule/*/edit' => TRUE,
+  );
+  return $paths;
+}
+
+/**
+ * Redefine overlay paths defined by other modules.
+ *
+ * @param $paths
+ *   An associative array of overlay paths, as defined by implementations of
+ *   hook_overlay_paths().
+ *
+ * @see hook_overlay_paths()
+ */
+function hook_overlay_paths_alter(&$paths) {
+  // Display all user pages in the overlay.
+  $paths['user/*'] = TRUE;
+  // Display the modules page in a regular browser window.
+  $paths['admin/modules'] = FALSE;
+}
+
+/**
  * @} End of "addtogroup hooks".
  */
diff --git a/core/modules/overlay/overlay.info b/core/modules/overlay/overlay.info
index a782792..67e5c8f 100644
--- a/core/modules/overlay/overlay.info
+++ b/core/modules/overlay/overlay.info
@@ -1,5 +1,5 @@
 name = Overlay
-description = Displays the Drupal administration interface in an overlay.
+description = Allows pages (e.g. the Drupal administration interface) to open in an overlay.
 package = Core
 version = VERSION
 core = 8.x
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index 8f62c01..d2b59cf 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Displays the Drupal administration interface in an overlay.
+ * Displays pages in a modal overlay.
  */
 
 /**
@@ -13,7 +13,7 @@ function overlay_help($path, $arg) {
     case 'admin/help#overlay':
       $output = '';
       $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('The Overlay module makes the administration pages on your site display in a JavaScript overlay of the page you were viewing when you clicked the administrative link, instead of replacing the page in your browser window. Use the close link on the overlay to return to the page you were viewing when you clicked the link. For more information, see the online handbook entry for <a href="@overlay">Overlay module</a>.', array('@overlay' => 'http://drupal.org/handbook/modules/overlay')) . '</p>';
+      $output .= '<p>' . t('The Overlay module allows certain pages on your site to be displayed in a JavaScript overlay of the page you were viewing when you clicked the link, instead of replacing the page in your browser window. By default, this behavior is enabled for adminstrative pages. Use the close link on the overlay to return to the page you were viewing when you clicked the link. For more information, see the online handbook entry for <a href="@overlay">Overlay module</a>.', array('@overlay' => 'http://drupal.org/handbook/modules/overlay')) . '</p>';
       return $output;
   }
 }
@@ -39,26 +39,13 @@ function overlay_menu() {
 }
 
 /**
- * Implements hook_admin_paths().
- */
-function overlay_admin_paths() {
-  $paths = array(
-    // This is marked as an administrative path so that if it is visited from
-    // within the overlay, the user will stay within the overlay while the
-    // callback is being processed.
-    'overlay/dismiss-message' => TRUE,
-  );
-  return $paths;
-}
-
-/**
  * Implements hook_permission().
  */
 function overlay_permission() {
   return array(
     'access overlay' => array(
-      'title' => t('Access the administrative overlay'),
-      'description' => t('View administrative pages in the overlay.'),
+      'title' => t('Access the overlay'),
+      'description' => t('View pages in the overlay.'),
     ),
   );
 }
@@ -86,14 +73,14 @@ function overlay_form_user_profile_form_alter(&$form, &$form_state) {
   if (user_access('access overlay', $account)) {
     $form['overlay_control'] = array(
       '#type' => 'fieldset',
-      '#title' => t('Administrative overlay'),
+      '#title' => t('Overlay'),
       '#weight' => 4,
       '#collapsible' => TRUE,
     );
     $form['overlay_control']['overlay'] = array(
       '#type' => 'checkbox',
-      '#title' => t('Use the overlay for administrative pages.'),
-      '#description' => t('Show administrative pages on top of the page you started from.'),
+      '#title' => t('Allow pages to open in the overlay.'),
+      '#description' => t('Allow certain links to open in an overlay on top of the page you started from.'),
       '#default_value' => isset($account->data['overlay']) ? $account->data['overlay'] : 1,
     );
   }
@@ -126,8 +113,8 @@ function overlay_init() {
   $use_overlay = !isset($user->data['overlay']) || $user->data['overlay'];
   if (empty($mode) && user_access('access overlay') && $use_overlay) {
     $current_path = current_path();
-    // After overlay is enabled on the modules page, redirect to
-    // <front>#overlay=admin/modules to actually enable the overlay.
+    // After overlay is enabled on admin/modules or admin/appearance, redirect to
+    // <front>#overlay=admin/... to actually enable the overlay.
     if (isset($_SESSION['overlay_enable_redirect']) && $_SESSION['overlay_enable_redirect']) {
       unset($_SESSION['overlay_enable_redirect']);
       drupal_goto('<front>', array('fragment' => 'overlay=' . $current_path));
@@ -142,7 +129,7 @@ function overlay_init() {
       }
       // If this page shouldn't be rendered inside the overlay, redirect to the
       // parent.
-      elseif (!path_is_admin($current_path)) {
+      elseif (!overlay_is_overlay_path($current_path)) {
         overlay_close_dialog($current_path, array('query' => drupal_get_query_parameters(NULL, array('q', 'render'))));
       }
 
@@ -152,8 +139,8 @@ function overlay_init() {
       // Unset the render parameter to avoid it being included in URLs on the page.
       unset($_GET['render']);
     }
-    // Do not enable the overlay if we already are on an admin page.
-    elseif (!path_is_admin($current_path)) {
+    // Do not enable the overlay if we already are on an overlay page.
+    elseif (!overlay_is_overlay_path($current_path)) {
       // Otherwise add overlay parent code and our behavior.
       overlay_set_mode('parent');
     }
@@ -339,7 +326,7 @@ function overlay_disable_message() {
       // Link to the user's profile page, where the overlay can be disabled.
       'profile_link' => array(
         '#type' => 'link',
-        '#title' => t('If you have problems accessing administrative pages on this site, disable the overlay on your profile page.'),
+        '#title' => t('If you have problems accessing this page, try disabling the overlay on your profile page.'),
         '#href' => 'user/' . $user->uid . '/edit',
         '#options' => array(
           'query' => drupal_get_destination(),
@@ -403,7 +390,7 @@ function theme_overlay_disable_message($variables) {
 
   // Add a heading for screen-reader users. The heading doesn't need to be seen
   // by sighted users.
-  $output = '<h3 class="element-invisible">' . t('Options for the administrative overlay') . '</h3>' . $output;
+  $output = '<h3 class="element-invisible">' . t('Options for the overlay') . '</h3>' . $output;
 
   // Wrap in a container for styling.
   $output = '<div id="overlay-disable-message" class="clearfix">' . $output . '</div>';
@@ -578,8 +565,8 @@ function overlay_get_mode() {
  * @param $mode
  *   To set the mode, pass in one of the following values:
  *   - 'parent': This is used in the context of a parent window (a regular
- *     browser window). If set, JavaScript is added so that administrative
- *     links in the parent window will open in an overlay.
+ *     browser window). If set, JavaScript is added to the links that should
+ *     open in an overlay.
  *   - 'child': This is used in the context of the child overlay window (the
  *     page actually appearing within the overlay iframe). If set, JavaScript
  *     and CSS are added so that Drupal behaves nicely from within the overlay.
@@ -632,8 +619,8 @@ function overlay_set_mode($mode = NULL) {
  * Implements hook_overlay_parent_initialize().
  */
 function overlay_overlay_parent_initialize() {
-  // Let the client side know which paths are administrative.
-  $paths = path_get_admin_paths();
+  // Let the client side know which paths should be displayed in the overlay.
+  $paths = overlay_paths();
   foreach ($paths as &$type) {
     $type = str_replace('<front>', variable_get('site_frontpage', 'user'), $type);
   }
@@ -701,6 +688,126 @@ function overlay_close_dialog($redirect = NULL, $redirect_options = array()) {
 }
 
 /**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Add a checkbox to the themes form specifying whether the overlay should be
+ * used for administrative pages.
+ *
+ * @see overlay_form_system_themes_admin_form_submit()
+ */
+function overlay_form_system_themes_admin_form_alter(&$form, &$form_state, $form_id) {
+  $form['admin_theme']['overlay_admin_paths'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Use the overlay for administrative pages.'),
+    '#default_value' => variable_get('overlay_admin_paths', TRUE),
+  );
+  $form['#submit'][] = 'overlay_form_system_themes_admin_form_submit';
+}
+
+/**
+ * Form submission handler for system_themes_admin_form().
+ *
+ * @see overlay_form_system_themes_admin_form_alter()
+ */
+function overlay_form_system_themes_admin_form_submit($form, &$form_state) {
+  if (!variable_get('overlay_admin_paths', TRUE) && $form_state['values']['overlay_admin_paths']) {
+    // When the overlay is being enabled on admin/appearance, open the page in
+    // the overlay.
+    $_SESSION['overlay_enable_redirect'] = 1;
+  }
+  variable_set('overlay_admin_paths', $form_state['values']['overlay_admin_paths']);
+}
+
+/**
+ * Implements hook_overlay_paths().
+ */
+function overlay_overlay_paths() {
+  $paths = array(
+    // If the path is visited from within the overlay, the user will stay within
+    // the overlay while the callback is being processed.
+    'overlay/dismiss-message' => TRUE,
+  );
+  return $paths;
+}
+
+/**
+ * Implements hook_overlay_paths_alter().
+ *
+ * Enabled the overlay for all administrative paths.
+ */
+function overlay_overlay_paths_alter(&$paths) {
+  if (variable_get('overlay_admin_paths', TRUE)) {
+    $admin_paths = path_get_admin_paths();
+    $paths += array_fill_keys(explode("\n", $admin_paths['admin']), TRUE);
+    $paths += array_fill_keys(explode("\n", $admin_paths['non_admin']), FALSE);
+  }
+}
+
+/**
+ * Get a list of paths that should and should not use overlay.
+ *
+ * @return array
+ *   An associative array containing the following keys:
+ *   'overlay': An array of overlay paths and regular expressions in a format
+ *              suitable for drupal_match_path().
+ *   'non_overlay': An array of non-overlay paths and regular expressions.
+ *
+ * @see hook_overlay_paths()
+ * @see hook_overlay_paths_alter()
+ * @see path_get_admin_paths()
+ */
+function overlay_paths() {
+  $patterns = &drupal_static(__FUNCTION__);
+  if (!isset($patterns)) {
+    $paths = module_invoke_all('overlay_paths');
+    drupal_alter('overlay_paths', $paths);
+    // Combine all overlay paths into one array, and likewise for non-overlay
+    // paths, for easier handling.
+    $patterns = array();
+    $patterns['overlay'] = array();
+    $patterns['non_overlay'] = array();
+    foreach ($paths as $path => $enabled) {
+      if ($enabled) {
+        $patterns['overlay'][] = $path;
+      }
+      else {
+        $patterns['non_overlay'][] = $path;
+      }
+    }
+    $patterns['overlay'] = implode("\n", $patterns['overlay']);
+    $patterns['non_overlay'] = implode("\n", $patterns['non_overlay']);
+  }
+  return $patterns;
+}
+
+/**
+ * Determine whether a path should be displayed in the overlay.
+ *
+ * By default, paths are considered to be non-overlay. If a path does not match
+ * any of the patterns in overlay_paths(), or if it matches both overlay and
+ * non-overlay patterns, it is considered non-overlay.
+ *
+ * @param $path
+ *   A Drupal path.
+ *
+ * @return
+ *   TRUE if the path should be displayed in the overlay, FALSE otherwise.
+ *
+ * @see overlay_paths()
+ * @see hook_overlay_paths()
+ * @see hook_overlay_paths_alter()
+ */
+function overlay_is_overlay_path($path) {
+  $path_map = &drupal_static(__FUNCTION__);
+  if (!isset($path_map['overlay'][$path])) {
+    $patterns = overlay_paths();
+    $path_map['overlay'][$path] = drupal_match_path($path, $patterns['overlay']);
+    $path_map['non_overlay'][$path] = drupal_match_path($path, $patterns['non_overlay']);
+  }
+  return $path_map['overlay'][$path] && !$path_map['non_overlay'][$path];
+}
+
+/**
  * Returns a list of page regions that appear in the overlay.
  *
  * Overlay regions correspond to the entire contents of the overlay child
@@ -724,7 +831,7 @@ function overlay_regions() {
  * parent window, but appear to the user as being related to the overlay
  * (usually because they are displayed next to, rather than underneath, the
  * main overlay regions) and therefore need to be dynamically refreshed if any
- * administrative actions taken within the overlay change their contents.
+ * actions taken within the overlay change their contents.
  *
  * An example of a typical overlay supplemental region would be the 'page_top'
  * region, in the case where a toolbar is being displayed there.
