diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index 9cfd1cf..48ecfe4 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -182,6 +182,22 @@ drupal.dialog.ajax:
     - core/drupal.ajax
     - core/drupal.dialog
 
+drupal.dialog.off_canvas:
+  version: VERSION
+  js:
+    misc/dialog/off-canvas.js: {}
+  css:
+    component:
+      misc/dialog/off-canvas.css: {}
+      misc/dialog/off-canvas.motion.css: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupal.ajax
+    - core/drupal.announce
+    - core/drupal.dialog
+    - core/drupal.dialog.ajax
+
 drupal.displace:
   version: VERSION
   js:
diff --git a/core/core.services.yml b/core/core.services.yml
index 8bce755..a7b1654 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1059,6 +1059,11 @@ services:
     arguments: ['@title_resolver']
     tags:
       - { name: render.main_content_renderer, format: drupal_dialog }
+  main_content_renderer.off_canvas:
+    class: Drupal\Core\Render\MainContent\OffCanvasRender
+    arguments: ['@title_resolver', '@renderer']
+    tags:
+      - { name: render.main_content_renderer, format: drupal_dialog.off_canvas }
   main_content_renderer.modal:
     class: Drupal\Core\Render\MainContent\ModalRenderer
     arguments: ['@title_resolver']
diff --git a/core/modules/outside_in/src/Ajax/OpenOffCanvasDialogCommand.php b/core/lib/Drupal/Core/Ajax/OpenOffCanvasDialogCommand.php
similarity index 87%
rename from core/modules/outside_in/src/Ajax/OpenOffCanvasDialogCommand.php
rename to core/lib/Drupal/Core/Ajax/OpenOffCanvasDialogCommand.php
index d78dc35..44cb569 100644
--- a/core/modules/outside_in/src/Ajax/OpenOffCanvasDialogCommand.php
+++ b/core/lib/Drupal/Core/Ajax/OpenOffCanvasDialogCommand.php
@@ -1,11 +1,9 @@
 <?php
 
-namespace Drupal\outside_in\Ajax;
-
-use Drupal\Core\Ajax\OpenDialogCommand;
+namespace Drupal\Core\Ajax;
 
 /**
- * Defines an AJAX command to open content in a dialog in a off-canvas tray.
+ * Defines an AJAX command to open content in a dialog in a off-canvas dialog.
  *
  * @ingroup ajax
  */
@@ -16,7 +14,7 @@ class OpenOffCanvasDialogCommand extends OpenDialogCommand {
    *
    * The off-canvas dialog differs from the normal modal provided by
    * OpenDialogCommand in that a off-canvas has built in positioning and
-   * behaviours. Drupal provides a built-in off-canvas tray for this purpose,
+   * behaviours. Drupal provides a built-in off-canvas dialog for this purpose,
    * so the selector is hard-coded in the call to the parent constructor.
    *
    * @param string $title
@@ -33,7 +31,7 @@ class OpenOffCanvasDialogCommand extends OpenDialogCommand {
    *   populated automatically from the current request.
    */
   public function __construct($title, $content, array $dialog_options = [], $settings = NULL) {
-    parent::__construct('#drupal-offcanvas', $title, $content, $dialog_options, $settings);
+    parent::__construct('#drupal-off-canvas', $title, $content, $dialog_options, $settings);
     $this->dialogOptions['modal'] = FALSE;
     $this->dialogOptions['autoResize'] = FALSE;
     $this->dialogOptions['resizable'] = 'w';
diff --git a/core/modules/outside_in/src/Render/MainContent/OffCanvasRender.php b/core/lib/Drupal/Core/Render/MainContent/OffCanvasRender.php
similarity index 86%
rename from core/modules/outside_in/src/Render/MainContent/OffCanvasRender.php
rename to core/lib/Drupal/Core/Render/MainContent/OffCanvasRender.php
index 5d24c09..c1eeaab 100644
--- a/core/modules/outside_in/src/Render/MainContent/OffCanvasRender.php
+++ b/core/lib/Drupal/Core/Render/MainContent/OffCanvasRender.php
@@ -1,17 +1,16 @@
 <?php
 
-namespace Drupal\outside_in\Render\MainContent;
+namespace Drupal\Core\Render\MainContent;
 
 use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\OpenOffCanvasDialogCommand;
 use Drupal\Core\Controller\TitleResolverInterface;
-use Drupal\Core\Render\MainContent\DialogRenderer;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\outside_in\Ajax\OpenOffCanvasDialogCommand;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
- * Default main content renderer for offcanvas dialog requests.
+ * Default main content renderer for off_canvas dialog requests.
  */
 class OffCanvasRender extends DialogRenderer {
 
@@ -46,7 +45,7 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
 
     // Attach the library necessary for using the OpenOffCanvasDialogCommand and
     // set the attachments for this Ajax response.
-    $main_content['#attached']['library'][] = 'outside_in/drupal.off_canvas';
+    $main_content['#attached']['library'][] = 'core/drupal.dialog.off_canvas';
     $response->setAttachments($main_content['#attached']);
 
     // If the main content doesn't provide a title, use the title resolver.
diff --git a/core/misc/ajax.js b/core/misc/ajax.js
index fefe9f3..524e978 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -64,6 +64,7 @@
           element_settings.event = 'click';
         }
         element_settings.dialogType = $(this).data('dialog-type');
+        element_settings.drupalDialogRenderer = $(this).data('dialog-renderer');
         element_settings.dialog = $(this).data('dialog-options');
         element_settings.base = $(this).attr('id');
         element_settings.element = this;
@@ -312,6 +313,8 @@
    *   Options for {@link Drupal.dialog}.
    * @prop {string} [dialogType]
    *   One of `'modal'` or `'dialog'`.
+   * @prop {string} [drupalDialogRenderer]
+   *   Renderer for the dialog, 'off_canvas' supported.
    * @prop {string} [prevent]
    *   List of events on which to stop default action and stop propagation.
    */
@@ -526,7 +529,11 @@
     else {
       ajax.options.url += '&';
     }
-    ajax.options.url += Drupal.ajax.WRAPPER_FORMAT + '=drupal_' + (element_settings.dialogType || 'ajax');
+    var wrapper = 'drupal_' + (element_settings.dialogType || 'ajax');
+    if (element_settings.drupalDialogRenderer) {
+      wrapper += '.' + element_settings.drupalDialogRenderer;
+    }
+    ajax.options.url += Drupal.ajax.WRAPPER_FORMAT + '=' + wrapper;
 
     // Bind the ajaxSubmit function to the element event.
     $(ajax.element).on(element_settings.event, function (event) {
diff --git a/core/misc/dialog/off-canvas.css b/core/misc/dialog/off-canvas.css
new file mode 100644
index 0000000..b649dab
--- /dev/null
+++ b/core/misc/dialog/off-canvas.css
@@ -0,0 +1,40 @@
+/**
+ * @file
+ * CSS for off-canvas dialog.
+ */
+
+/* Position the off-canvas dialog container outside the right of the viewport. */
+.ui-dialog-off-canvas {
+  box-sizing: border-box;
+  height: 100%;
+  overflow: visible;
+}
+
+/* Wrap the form that's inside the off-canvas dialog. */
+.ui-dialog-off-canvas .ui-dialog-content {
+  padding: 0 20px;
+  /* Prevent horizontal scrollbar. */
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+[dir="rtl"] .ui-dialog-off-canvas .ui-dialog-content {
+  text-align: right;
+}
+
+/**
+ * Force the tray to be 100% width at the same breakpoint the dialog system uses
+ * to expand dialog widths.
+ */
+@media all and (max-width: 48em) {
+  /* 768px */
+  .ui-dialog.ui-dialog-off-canvas {
+    width: 100% !important;
+  }
+
+  /* When tray is at 100% width stop the body from scrolling */
+  .js-tray-open {
+    height: 100%;
+    overflow-y: hidden;
+  }
+}
diff --git a/core/modules/outside_in/js/offcanvas.js b/core/misc/dialog/off-canvas.js
similarity index 72%
rename from core/modules/outside_in/js/offcanvas.js
rename to core/misc/dialog/off-canvas.js
index 8cda69d..3d37fd6 100644
--- a/core/modules/outside_in/js/offcanvas.js
+++ b/core/misc/dialog/off-canvas.js
@@ -1,10 +1,6 @@
 /**
  * @file
  * Drupal's off-canvas library.
- *
- * @todo This functionality should extracted into a new core library or a part
- *  of the current drupal.dialog.ajax library.
- *  https://www.drupal.org/node/2784443
  */
 
 (function ($, Drupal, debounce, displace) {
@@ -12,7 +8,7 @@
   'use strict';
 
   // The minimum width to use body displace needs to match the width at which
-  // the tray will be %100 width. @see outside_in.module.css
+  // the tray will be %100 width. @see off-canvas.css
   var minDisplaceWidth = 768;
 
   /**
@@ -22,7 +18,7 @@
    */
   var edge = document.documentElement.dir === 'rtl' ? 'left' : 'right';
 
-  var $mainCanvasWrapper = $('[data-offcanvas-main-canvas]');
+  var $mainCanvasWrapper = $('[data-off-canvas-main-canvas]');
 
   /**
    * Resets the size of the dialog.
@@ -51,7 +47,7 @@
 
     $element
       .dialog('option', adjustedOptions)
-      .trigger('dialogContentResize.offcanvas');
+      .trigger('dialogContentResize.off-canvas');
   }
 
   /**
@@ -64,7 +60,7 @@
     var $element = event.data.$element;
     var $widget = $element.dialog('widget');
 
-    var $offsets = $widget.find('> :not(#drupal-offcanvas, .ui-resizable-handle)');
+    var $offsets = $widget.find('> :not(#drupal-off-canvas, .ui-resizable-handle)');
     var offset = 0;
     var modalHeight;
 
@@ -102,25 +98,25 @@
 
   $(window).on({
     'dialog:aftercreate': function (event, dialog, $element, settings) {
-      if ($element.is('#drupal-offcanvas')) {
+      if ($element.is('#drupal-off-canvas')) {
         var eventData = {settings: settings, $element: $element};
-        $('.ui-dialog-offcanvas, .ui-dialog-offcanvas .ui-dialog-titlebar').toggleClass('ui-dialog-empty-title', !settings.title);
+        $('.ui-dialog-off-canvas, .ui-dialog-off-canvas .ui-dialog-titlebar').toggleClass('ui-dialog-empty-title', !settings.title);
 
         $element
-          .on('dialogresize.offcanvas', eventData, debounce(bodyPadding, 100))
-          .on('dialogContentResize.offcanvas', eventData, handleDialogResize)
-          .on('dialogContentResize.offcanvas', eventData, debounce(bodyPadding, 100))
-          .trigger('dialogresize.offcanvas');
+          .on('dialogresize.off-canvas', eventData, debounce(bodyPadding, 100))
+          .on('dialogContentResize.off-canvas', eventData, handleDialogResize)
+          .on('dialogContentResize.off-canvas', eventData, debounce(bodyPadding, 100))
+          .trigger('dialogresize.off-canvas');
 
         $element.dialog('widget').attr('data-offset-' + edge, '');
 
         $(window)
-          .on('resize.offcanvas scroll.offcanvas', eventData, debounce(resetSize, 100))
-          .trigger('resize.offcanvas');
+          .on('resize.off-canvas scroll.off-canvas', eventData, debounce(resetSize, 100))
+          .trigger('resize.off-canvas');
       }
     },
     'dialog:beforecreate': function (event, dialog, $element, settings) {
-      if ($element.is('#drupal-offcanvas')) {
+      if ($element.is('#drupal-off-canvas')) {
         $('body').addClass('js-tray-open');
         // @see http://api.jqueryui.com/position/
         settings.position = {
@@ -128,17 +124,17 @@
           at: edge + ' top',
           of: window
         };
-        settings.dialogClass += ' ui-dialog-offcanvas';
+        settings.dialogClass += ' ui-dialog-off-canvas';
         // Applies initial height to dialog based on window height.
         // See http://api.jqueryui.com/dialog for all dialog options.
         settings.height = $(window).height();
       }
     },
     'dialog:beforeclose': function (event, dialog, $element) {
-      if ($element.is('#drupal-offcanvas')) {
+      if ($element.is('#drupal-off-canvas')) {
         $('body').removeClass('js-tray-open');
-        $(document).off('.offcanvas');
-        $(window).off('.offcanvas');
+        $(document).off('.off-canvas');
+        $(window).off('.off-canvas');
         $mainCanvasWrapper.css('padding-' + edge, 0);
       }
     }
diff --git a/core/misc/dialog/off-canvas.motion.css b/core/misc/dialog/off-canvas.motion.css
new file mode 100644
index 0000000..db5b6ef
--- /dev/null
+++ b/core/misc/dialog/off-canvas.motion.css
@@ -0,0 +1,31 @@
+/**
+ * @file
+ * Motion effects for off-canvas dialog.
+ *
+ * Motion effects are in a separate file so that they can be easily turned off
+ * to improve performance if desired.
+ *
+ * @todo Add a configuration option for browser rendering performance to disable
+ *   this file: https://www.drupal.org/node/2784443.
+ */
+
+/* Transition the off-canvas dialog container, with 2s delay to match main canvas speed. */
+.ui-dialog-off-canvas .ui-dialog-content {
+  -webkit-transition: all .7s ease 2s;
+  -moz-transition: all .7s ease 2s;
+  transition: all .7s ease 2s;
+}
+
+@media (max-width: 700px) {
+  .ui-dialog-off-canvas .ui-dialog-content {
+    -webkit-transition: all .7s ease;
+    -moz-transition: all .7s ease;
+    transition: all .7s ease;
+  }
+}
+
+.dialog-off-canvas__main-canvas {
+  -webkit-transition: all .7s ease;
+  -moz-transition: all .7s ease;
+  transition: all .7s ease;
+}
diff --git a/core/modules/outside_in/css/offcanvas.css b/core/modules/outside_in/css/offcanvas.css
deleted file mode 100644
index fb671db..0000000
--- a/core/modules/outside_in/css/offcanvas.css
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * @file
- * CSS for Offcanvas tray.
- *
- * @todo Move CSS into core dialog library https://www.drupal.org/node/2784443.
- */
-/* Position the dialog-offcanvas tray container outside the right of the viewport. */
-.ui-dialog-offcanvas {
-    box-sizing: border-box;
-    height: 100%;
-    overflow: visible;
-}
-
-/* Wrap the form that's inside the dialog-offcanvas tray. */
-.ui-dialog-offcanvas .ui-dialog-content {
-    padding: 0 20px;
-    /* Prevent horizontal scrollbar. */
-    overflow-x: hidden;
-    overflow-y: auto;
-}
-[dir="rtl"] .ui-dialog-offcanvas .ui-dialog-content {
-    text-align: right;
-}
diff --git a/core/modules/outside_in/css/offcanvas.motion.css b/core/modules/outside_in/css/offcanvas.motion.css
deleted file mode 100644
index 1c06994..0000000
--- a/core/modules/outside_in/css/offcanvas.motion.css
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * @file
- * Motion effects for off-canvas tray dialog.
- *
- * Motion effects are in a separate file so that they can be easily turned off
- * to improve performance if desired.
- *
- * @todo Move motion effects file into a core Off-Canvas library and add a
- *   configuration option for browser rendering performance to disable this
- *   file: https://www.drupal.org/node/2784443.
- */
-
-/* Transition the dialog-offcanvas tray container, with 2s delay to match main canvas speed. */
-.ui-dialog-offcanvas .ui-dialog-content {
-    -webkit-transition: all .7s ease 2s;
-    -moz-transition: all .7s ease 2s;
-    transition: all .7s ease 2s;
-}
-
-@media (max-width: 700px) {
-    .ui-dialog-offcanvas .ui-dialog-content {
-        -webkit-transition: all .7s ease;
-        -moz-transition: all .7s ease;
-        transition: all .7s ease;
-    }
-}
-
-.dialog-offcanvas__main-canvas {
-  -webkit-transition: all .7s ease;
-  -moz-transition: all .7s ease;
-  transition: all .7s ease;
-}
diff --git a/core/modules/outside_in/css/outside_in.details.css b/core/modules/outside_in/css/outside_in.details.css
index 8ca1406..281c33d 100644
--- a/core/modules/outside_in/css/outside_in.details.css
+++ b/core/modules/outside_in/css/outside_in.details.css
@@ -1,6 +1,6 @@
 /**
  * @file
- * Visual styling for summary and details in the Settings Tray module's off canvas tray.
+ * Visual styling for summary and details in the Settings Tray module's off-canvas dialog.
  */
 
 .ui-dialog-outside-in details,
diff --git a/core/modules/outside_in/css/outside_in.form.css b/core/modules/outside_in/css/outside_in.form.css
index 5d4c3be..bba0150 100644
--- a/core/modules/outside_in/css/outside_in.form.css
+++ b/core/modules/outside_in/css/outside_in.form.css
@@ -1,6 +1,6 @@
 /**
  * @file
- * Visual styling for forms in the Settings Tray module's off canvas tray.
+ * Visual styling for forms in the Settings Tray module's off-canvas dialog.
  */
 
 .ui-dialog-outside-in label {
diff --git a/core/modules/outside_in/css/outside_in.module.css b/core/modules/outside_in/css/outside_in.module.css
index 5ac00ab..2022837 100644
--- a/core/modules/outside_in/css/outside_in.module.css
+++ b/core/modules/outside_in/css/outside_in.module.css
@@ -21,18 +21,3 @@
 #main-canvas.js-outside-in-edit-mode .contextual-links a {
   pointer-events: inherit;
 }
-
-/*
- * Force the tray to be 100% width at the same breakpoint the dialog system uses
- * to expand dialog widths.
- */
-@media all and (max-width: 48em) { /* 768px */
-  .ui-dialog.ui-dialog-offcanvas {
-    width: 100% !important;
-  }
-  /* When tray is at 100% width stop the body from scrolling */
-  .js-tray-open {
-    height: 100%;
-    overflow-y: hidden;
-  }
-}
diff --git a/core/modules/outside_in/css/outside_in.motion.css b/core/modules/outside_in/css/outside_in.motion.css
index 450bffe..61d68b7 100644
--- a/core/modules/outside_in/css/outside_in.motion.css
+++ b/core/modules/outside_in/css/outside_in.motion.css
@@ -4,10 +4,6 @@
  *
  * Motion effects are in a separate file so that they can be easily turned off
  * to improve performance if desired.
- *
- * @todo Move motion effects file into a core Off-Canvas library and add a
- *   configuration option for browser rendering performance to disable this
- *   file: https://www.drupal.org/node/2784443.
  */
 
 
@@ -19,9 +15,9 @@
 }
 
 /* Transition the editables on the page, their contextual links and their hover states. */
-.dialog-offcanvas__main-canvas .contextual,
-.dialog-offcanvas__main-canvas .js-outside-in-edit-mode .outside-in-editable,
-.dialog-offcanvas__main-canvas.js-tray-open .js-outside-in-edit-mode .outside-in-editable {
+.dialog-off-canvas__main-canvas .contextual,
+.dialog-off-canvas__main-canvas .js-outside-in-edit-mode .outside-in-editable,
+.dialog-off-canvas__main-canvas.js-tray-open .js-outside-in-edit-mode .outside-in-editable {
   -webkit-transition: all .7s ease;
   -moz-transition: all .7s ease;
   transition: all .7s ease;
diff --git a/core/modules/outside_in/css/outside_in.table.css b/core/modules/outside_in/css/outside_in.table.css
index dc6bb89..51c3a2a 100644
--- a/core/modules/outside_in/css/outside_in.table.css
+++ b/core/modules/outside_in/css/outside_in.table.css
@@ -1,6 +1,6 @@
 /**
  * @file
- * Visual styling for tables in the Settings Tray module's off canvas tray.
+ * Visual styling for tables in the Settings Tray module's off-canvas dialog.
  */
 
 .ui-dialog-outside-in table {
diff --git a/core/modules/outside_in/css/outside_in.theme.css b/core/modules/outside_in/css/outside_in.theme.css
index 91c01ab..7a63a53 100644
--- a/core/modules/outside_in/css/outside_in.theme.css
+++ b/core/modules/outside_in/css/outside_in.theme.css
@@ -60,16 +60,16 @@
 }
 
 /* Style the editables while in edit mode. */
-.dialog-offcanvas__main-canvas.js-outside-in-edit-mode .outside-in-editable {
+.dialog-off-canvas__main-canvas.js-outside-in-edit-mode .outside-in-editable {
   outline: 1px dashed rgba(0,0,0,0.5);
   box-shadow: 0 0 0 1px rgba(255,255,255,0.7);
 }
-.dialog-offcanvas__main-canvas.js-outside-in-edit-mode .outside-in-editable:hover,
-.dialog-offcanvas__main-canvas.js-outside-in-edit-mode .outside-in-editable.outside-in-active-editable {
+.dialog-off-canvas__main-canvas.js-outside-in-edit-mode .outside-in-editable:hover,
+.dialog-off-canvas__main-canvas.js-outside-in-edit-mode .outside-in-editable.outside-in-active-editable {
   background-color: rgba(0,0,0,0.2);
 }
 
-/* Style the dialog-offcanvas container. */
+/* Style the dialog-off-canvas container. */
 .ui-dialog-outside-in {
   background: #444;
   border: 0 solid transparent;
@@ -197,7 +197,7 @@
 /* Groups below here with todos to move to new component need to load last. */
 
 /**
- * Visual styling for buttons in the Settings Tray module's off canvas tray.
+ * Visual styling for buttons in the Settings Tray module's off-canvas dialog.
  * @todo Move to its own component:
  *   https://www.drupal.org/node/1945262.
  */
@@ -237,7 +237,7 @@
 
 
 /*
- * Visual styling for dropbutton in the Settings Tray module's off canvas tray.
+ * Visual styling for dropbutton in the Settings Tray module's off-canvas dialog.
  * @todo Move to its own component:
  *   https://www.drupal.org/node/1945262.
  */
diff --git a/core/modules/outside_in/js/outside_in.js b/core/modules/outside_in/js/outside_in.js
index 0b8bc88..d51f31b 100644
--- a/core/modules/outside_in/js/outside_in.js
+++ b/core/modules/outside_in/js/outside_in.js
@@ -9,7 +9,7 @@
 
   var blockConfigureSelector = '[data-outside-in-edit]';
   var toggleEditSelector = '[data-drupal-outsidein="toggle"]';
-  var itemsToToggleSelector = '[data-offcanvas-main-canvas], #toolbar-bar, [data-drupal-outsidein="editable"] a, [data-drupal-outsidein="editable"] button';
+  var itemsToToggleSelector = '[data-off-canvas-main-canvas], #toolbar-bar, [data-drupal-outsidein="editable"] a, [data-drupal-outsidein="editable"] button';
   var contextualItemsSelector = '[data-contextual-id] a, [data-contextual-id] button';
   var quickEditItemSelector = '[data-quickedit-entity-id]';
 
@@ -111,10 +111,10 @@
   }
 
   /**
-   * Closes/removes offcanvas.
+   * Closes/removes off-canvas dialog.
    */
   function closeOffCanvas() {
-    $('.ui-dialog-offcanvas .ui-dialog-titlebar-close').trigger('click');
+    $('.ui-dialog-off-canvas .ui-dialog-titlebar-close').trigger('click');
   }
 
   /**
@@ -135,7 +135,7 @@
       $editables = $('[data-drupal-outsidein="editable"]').once('outsidein');
       if ($editables.length) {
         // Use event capture to prevent clicks on links.
-        document.querySelector('[data-offcanvas-main-canvas]').addEventListener('click', preventClick, true);
+        document.querySelector('[data-off-canvas-main-canvas]').addEventListener('click', preventClick, true);
 
         // When a click occurs try and find the outside-in edit link
         // and click it.
@@ -152,7 +152,7 @@
         $(quickEditItemSelector)
           .not(contextualItemsSelector)
           .on('click.outsidein', function (e) {
-            // For all non-contextual links or the contextual QuickEdit link close the off-canvas tray.
+            // For all non-contextual links or the contextual QuickEdit link close the off-canvas dialog.
             if (!$(e.target).parent().hasClass('contextual') || $(e.target).parent().hasClass('quickedit')) {
               closeOffCanvas();
             }
@@ -168,7 +168,7 @@
     else {
       $editables = $('[data-drupal-outsidein="editable"]').removeOnce('outsidein');
       if ($editables.length) {
-        document.querySelector('[data-offcanvas-main-canvas]').removeEventListener('click', preventClick, true);
+        document.querySelector('[data-off-canvas-main-canvas]').removeEventListener('click', preventClick, true);
         $editables.off('.outsidein');
         $(quickEditItemSelector).off('.outsidein');
       }
@@ -210,26 +210,17 @@
     attach: function () {
 
       $(toggleEditSelector).once('outsidein').on('click.outsidein', toggleEditMode);
-
-      var search = Drupal.ajax.WRAPPER_FORMAT + '=drupal_dialog';
-      var replace = Drupal.ajax.WRAPPER_FORMAT + '=drupal_dialog_offcanvas';
-      // Loop through all Ajax links and change the format to dialog-offcanvas when
-      // needed.
+      // Loop through all Ajax links to set active editable ID.
       Drupal.ajax.instances
         .filter(function (instance) {
           var hasElement = instance && !!instance.element;
-          var rendererOffcanvas = false;
-          var wrapperOffcanvas = false;
+          var rendererOffCanvas = false;
           if (hasElement) {
-            rendererOffcanvas = $(instance.element).attr('data-dialog-renderer') === 'offcanvas';
-            wrapperOffcanvas = instance.options.url.indexOf('drupal_dialog_offcanvas') === -1;
+            rendererOffCanvas = $(instance.element).attr('data-dialog-renderer') === 'off_canvas';
           }
-          return hasElement && rendererOffcanvas && wrapperOffcanvas;
+          return hasElement && rendererOffCanvas;
         })
         .forEach(function (instance) {
-          // @todo Move logic for data-dialog-renderer attribute into ajax.js
-          //   https://www.drupal.org/node/2784443
-          instance.options.url = instance.options.url.replace(search, replace);
           // Check to make sure existing dialogOptions aren't overridden.
           if (!('dialogOptions' in instance.options.data)) {
             instance.options.data.dialogOptions = {};
@@ -243,7 +234,7 @@
   // Manage Active editable class on opening and closing of the dialog.
   $(window).on({
     'dialog:beforecreate': function (event, dialog, $element, settings) {
-      if ($element.is('#drupal-offcanvas')) {
+      if ($element.is('#drupal-off-canvas')) {
         $('body .outside-in-active-editable').removeClass('outside-in-active-editable');
         var $activeElement = $('#' + settings.outsideInActiveEditableId);
         if ($activeElement.length) {
@@ -253,7 +244,7 @@
       }
     },
     'dialog:beforeclose': function (event, dialog, $element) {
-      if ($element.is('#drupal-offcanvas')) {
+      if ($element.is('#drupal-off-canvas')) {
         $('body .outside-in-active-editable').removeClass('outside-in-active-editable');
       }
     }
diff --git a/core/modules/outside_in/outside_in.libraries.yml b/core/modules/outside_in/outside_in.libraries.yml
index 5eddea4..cf12dc0 100644
--- a/core/modules/outside_in/outside_in.libraries.yml
+++ b/core/modules/outside_in/outside_in.libraries.yml
@@ -20,14 +20,3 @@ drupal.outside_in:
     - core/drupal
     - core/jquery.once
     - core/drupal.ajax
-drupal.off_canvas:
-  version: VERSION
-  js:
-    js/offcanvas.js: {}
-  dependencies:
-    - core/jquery
-    - core/drupal
-    - core/drupal.ajax
-    - core/drupal.announce
-    - core/drupal.dialog
-    - core/drupal.dialog.ajax
diff --git a/core/modules/outside_in/outside_in.links.contextual.yml b/core/modules/outside_in/outside_in.links.contextual.yml
index 99b36ef..b0c0f13 100644
--- a/core/modules/outside_in/outside_in.links.contextual.yml
+++ b/core/modules/outside_in/outside_in.links.contextual.yml
@@ -1,4 +1,4 @@
 outside_in.block_configure:
   title: 'Quick edit'
-  route_name: 'entity.block.offcanvas_form'
+  route_name: 'entity.block.off_canvas_form'
   group: 'block'
diff --git a/core/modules/outside_in/outside_in.module b/core/modules/outside_in/outside_in.module
index a7fab7b..186c5e7 100644
--- a/core/modules/outside_in/outside_in.module
+++ b/core/modules/outside_in/outside_in.module
@@ -30,18 +30,18 @@ function outside_in_help($route_name, RouteMatchInterface $route_match) {
 /**
  * Implements hook_contextual_links_view_alter().
  *
- * Change Configure Blocks into offcanvas links.
+ * Change Configure Blocks into off-canvas links.
  */
 function outside_in_contextual_links_view_alter(&$element, $items) {
   if (isset($element['#links']['outside-inblock-configure'])) {
     $element['#links']['outside-inblock-configure']['attributes'] = [
       'class' => ['use-ajax'],
       'data-dialog-type' => 'dialog',
-      'data-dialog-renderer' => 'offcanvas',
+      'data-dialog-renderer' => 'off_canvas',
       'data-outside-in-edit' => TRUE,
     ];
 
-    $element['#attached']['library'][] = 'outside_in/drupal.off_canvas';
+    $element['#attached']['library'][] = 'core/drupal.dialog.off_canvas';
   }
 }
 
@@ -58,33 +58,13 @@ function outside_in_block_view_alter(array &$build) {
 }
 
 /**
- * Implements hook_element_info_alter().
- */
-function outside_in_element_info_alter(&$type) {
-  if (isset($type['page'])) {
-    $type['page']['#theme_wrappers']['outside_in_page_wrapper'] = ['#weight' => -1000];
-  }
-}
-
-/**
- * Implements hook_theme().
- */
-function outside_in_theme() {
-  return [
-    'outside_in_page_wrapper' => [
-      'variables' => ['children' => NULL],
-    ],
-  ];
-}
-
-/**
  * Implements hook_entity_type_build().
  */
 function outside_in_entity_type_build(array &$entity_types) {
   /* @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
   $entity_types['block']
-    ->setFormClass('offcanvas', BlockEntityOffCanvasForm::class)
-    ->setLinkTemplate('offcanvas-form', '/admin/structure/block/manage/{block}/offcanvas');
+    ->setFormClass('off_canvas', BlockEntityOffCanvasForm::class)
+    ->setLinkTemplate('off_canvas-form', '/admin/structure/block/manage/{block}/off-canvas');
 }
 
 /**
@@ -130,14 +110,14 @@ function outside_in_toolbar_alter(&$items) {
  */
 function outside_in_block_alter(&$definitions) {
   if (!empty($definitions['system_branding_block'])) {
-    $definitions['system_branding_block']['forms']['offcanvas'] = SystemBrandingOffCanvasForm::class;
+    $definitions['system_branding_block']['forms']['off_canvas'] = SystemBrandingOffCanvasForm::class;
   }
 
   // Since menu blocks use derivatives, check the definition ID instead of
   // relying on the plugin ID.
   foreach ($definitions as &$definition) {
     if ($definition['id'] === 'system_menu_block') {
-      $definition['forms']['offcanvas'] = SystemMenuOffCanvasForm::class;
+      $definition['forms']['off_canvas'] = SystemMenuOffCanvasForm::class;
     }
   }
 }
diff --git a/core/modules/outside_in/outside_in.routing.yml b/core/modules/outside_in/outside_in.routing.yml
index 79bc38d..18a564c 100644
--- a/core/modules/outside_in/outside_in.routing.yml
+++ b/core/modules/outside_in/outside_in.routing.yml
@@ -1,7 +1,7 @@
-entity.block.offcanvas_form:
-  path: '/admin/structure/block/manage/{block}/offcanvas'
+entity.block.off_canvas_form:
+  path: '/admin/structure/block/manage/{block}/off-canvas'
   defaults:
-    _entity_form: 'block.offcanvas'
+    _entity_form: 'block.off_canvas'
     _title_callback: '\Drupal\outside_in\Block\BlockEntityOffCanvasForm::title'
   requirements:
     _permission: 'administer blocks'
diff --git a/core/modules/outside_in/outside_in.services.yml b/core/modules/outside_in/outside_in.services.yml
index 48f5824..c678291 100644
--- a/core/modules/outside_in/outside_in.services.yml
+++ b/core/modules/outside_in/outside_in.services.yml
@@ -1,10 +1,4 @@
 services:
-  main_content_renderer.off_canvas:
-    class: Drupal\outside_in\Render\MainContent\OffCanvasRender
-    arguments: ['@title_resolver', '@renderer']
-    tags:
-      - { name: render.main_content_renderer, format: drupal_dialog_offcanvas }
-
   outside_in.manager:
     class: Drupal\outside_in\OutsideInManager
     arguments: ['@router.admin_context', '@current_route_match', '@current_user']
diff --git a/core/modules/outside_in/src/Block/BlockEntityOffCanvasForm.php b/core/modules/outside_in/src/Block/BlockEntityOffCanvasForm.php
index a77f51f..4e0f081 100644
--- a/core/modules/outside_in/src/Block/BlockEntityOffCanvasForm.php
+++ b/core/modules/outside_in/src/Block/BlockEntityOffCanvasForm.php
@@ -9,7 +9,7 @@
 use Drupal\Core\Plugin\PluginWithFormsInterface;
 
 /**
- * Provides form for block instance forms when used in the off-canvas tray.
+ * Provides form for block instance forms when used in the off-canvas dialog.
  *
  * This form removes advanced sections of regular block form such as the
  * visibility settings, machine ID and region.
@@ -92,7 +92,7 @@ protected function submitVisibility(array $form, FormStateInterface $form_state)
    */
   protected function getPluginForm(BlockPluginInterface $block) {
     if ($block instanceof PluginWithFormsInterface) {
-      return $this->pluginFormFactory->createInstance($block, 'offcanvas', 'configure');
+      return $this->pluginFormFactory->createInstance($block, 'off_canvas', 'configure');
     }
     return $block;
   }
diff --git a/core/modules/outside_in/tests/modules/offcanvas_test/offcanvas_test.routing.yml b/core/modules/outside_in/tests/modules/offcanvas_test/offcanvas_test.routing.yml
deleted file mode 100644
index 761693b..0000000
--- a/core/modules/outside_in/tests/modules/offcanvas_test/offcanvas_test.routing.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-offcanvas_test.links:
-  path: '/offcanvas-test-links'
-  defaults:
-    _controller: '\Drupal\offcanvas_test\Controller\TestController::linksDisplay'
-    _title: 'Links'
-  requirements:
-    _access: 'TRUE'
-
-offcanvas_test.thing1:
-  path: '/offcanvas-thing1'
-  defaults:
-    _controller: '\Drupal\offcanvas_test\Controller\TestController::thing1'
-    _title: 'Thing 1'
-  requirements:
-    _access: 'TRUE'
-
-offcanvas_test.thing2:
-  path: '/offcanvas-thing2'
-  defaults:
-    _controller: '\Drupal\offcanvas_test\Controller\TestController::thing2'
-  requirements:
-    _access: 'TRUE'
-
-offcanvas_test.dialog_links:
-  path: '/offcanvas-dialog-links'
-  defaults:
-    _controller: '\Drupal\offcanvas_test\Controller\TestController::otherDialogLinks'
-  requirements:
-    _access: 'TRUE'
diff --git a/core/modules/outside_in/tests/modules/offcanvas_test/src/Plugin/Block/TestBlock.php b/core/modules/outside_in/tests/modules/offcanvas_test/src/Plugin/Block/TestBlock.php
deleted file mode 100644
index efb38e0..0000000
--- a/core/modules/outside_in/tests/modules/offcanvas_test/src/Plugin/Block/TestBlock.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-namespace Drupal\offcanvas_test\Plugin\Block;
-
-use Drupal\Core\Block\BlockBase;
-use Drupal\Core\Url;
-
-/**
- * Provides an 'Off-canvas test block' block.
- *
- * @Block(
- *   id = "offcanvas_links_block",
- *   admin_label = @Translation("Off-canvas test block")
- * )
- */
-class TestBlock extends BlockBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function build() {
-    return [
-      'offcanvas_link_1' => [
-        '#title' => $this->t('Click Me 1!'),
-        '#type' => 'link',
-        '#url' => Url::fromRoute('offcanvas_test.thing1'),
-        '#attributes' => [
-          'class' => ['use-ajax'],
-          'data-dialog-type' => 'offcanvas',
-        ],
-      ],
-      'offcanvas_link_2' => [
-        '#title' => $this->t('Click Me 2!'),
-        '#type' => 'link',
-        '#url' => Url::fromRoute('offcanvas_test.thing2'),
-        '#attributes' => [
-          'class' => ['use-ajax'],
-          'data-dialog-type' => 'offcanvas',
-        ],
-      ],
-      '#attached' => [
-        'library' => [
-          'outside_in/drupal.off_canvas',
-        ],
-      ],
-    ];
-  }
-
-}
diff --git a/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php b/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php
index c39f2f8..ac20108 100644
--- a/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php
+++ b/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInBlockFormTest.php
@@ -5,7 +5,7 @@
 use Drupal\user\Entity\Role;
 
 /**
- * Testing opening and saving block forms in the off-canvas tray.
+ * Testing opening and saving block forms in the off-canvas dialog.
  *
  * @group outside_in
  */
@@ -53,7 +53,7 @@ protected function setUp() {
   }
 
   /**
-   * Tests opening Offcanvas tray by click blocks and elements in the blocks.
+   * Tests opening off-canvas dialog by click blocks and elements in the blocks.
    *
    * @dataProvider providerTestBlocks
    */
@@ -119,14 +119,14 @@ public function testBlocks($block_id, $new_page_text, $element_selector, $label_
 
     // Exit edit mode using ESC.
     $web_assert->elementTextContains('css', '.contextual-toolbar-tab button', 'Editing');
-    $web_assert->elementAttributeContains('css', '.dialog-offcanvas__main-canvas', 'class', 'js-outside-in-edit-mode');
+    $web_assert->elementAttributeContains('css', '.dialog-off-canvas__main-canvas', 'class', 'js-outside-in-edit-mode');
     // Simulate press the Escape key.
     $this->getSession()->executeScript('jQuery("body").trigger(jQuery.Event("keyup", { keyCode: 27 }));');
     $this->waitForOffCanvasToClose();
     $this->getSession()->wait(100);
     $web_assert->elementTextContains('css', '#drupal-live-announce', 'Exited edit mode.');
     $web_assert->elementTextNotContains('css', '.contextual-toolbar-tab button', 'Editing');
-    $web_assert->elementAttributeNotContains('css', '.dialog-offcanvas__main-canvas', 'class', 'js-outside-in-edit-mode');
+    $web_assert->elementAttributeNotContains('css', '.dialog-off-canvas__main-canvas', 'class', 'js-outside-in-edit-mode');
   }
 
   /**
@@ -258,11 +258,11 @@ public function testQuickEditLinks() {
       $this->openBlockForm($block_selector);
       $page->find('css', $body_selector)->click();
       $this->waitForElement($quick_edit_selector);
-      // Offcanvas should be closed when opening QuickEdit toolbar.
+      // Off-canvas dialog should be closed when opening QuickEdit toolbar.
       $this->waitForOffCanvasToClose();
 
       $this->openBlockForm($block_selector);
-      // QuickEdit toolbar should be closed when opening Offcanvas.
+      // QuickEdit toolbar should be closed when opening off-canvas dialog.
       $web_assert->elementNotExists('css', $quick_edit_selector);
     }
 
@@ -276,7 +276,7 @@ public function testQuickEditLinks() {
     // Open off-canvas and enter Edit mode via contextual link.
     $this->clickContextualLink($block_selector, "Quick edit");
     $this->waitForOffCanvasToOpen();
-    // QuickEdit toolbar should be closed when opening Offcanvas.
+    // QuickEdit toolbar should be closed when opening off-canvas dialog.
     $web_assert->elementNotExists('css', $quick_edit_selector);
     // Open QuickEdit toolbar via contextual link while in Edit mode.
     $this->clickContextualLink('.node', "Quick edit", FALSE);
diff --git a/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInJavascriptTestBase.php b/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInJavascriptTestBase.php
index 48ba899..a202211 100644
--- a/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInJavascriptTestBase.php
+++ b/core/modules/outside_in/tests/src/FunctionalJavascript/OutsideInJavascriptTestBase.php
@@ -2,92 +2,12 @@
 
 namespace Drupal\Tests\outside_in\FunctionalJavascript;
 
-use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\Tests\system\FunctionalJavascript\OffCanvasTestBase;
 
 /**
  * Base class contains common test functionality for the Settings Tray module.
  */
-abstract class OutsideInJavascriptTestBase extends JavascriptTestBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function drupalGet($path, array $options = array(), array $headers = array()) {
-    $return = parent::drupalGet($path, $options, $headers);
-
-    // After the page loaded we need to additionally wait until the settings
-    // tray Ajax activity is done.
-    $this->assertSession()->assertWaitOnAjaxRequest();
-
-    return $return;
-  }
-
-  /**
-   * Enables a theme.
-   *
-   * @param string $theme
-   *   The theme.
-   */
-  public function enableTheme($theme) {
-    // Enable the theme.
-    \Drupal::service('theme_installer')->install([$theme]);
-    $theme_config = \Drupal::configFactory()->getEditable('system.theme');
-    $theme_config->set('default', $theme);
-    $theme_config->save();
-  }
-
-  /**
-   * Waits for Off-canvas tray to open.
-   */
-  protected function waitForOffCanvasToOpen() {
-    $web_assert = $this->assertSession();
-    $web_assert->assertWaitOnAjaxRequest();
-    $this->waitForElement('#drupal-offcanvas');
-  }
-
-  /**
-   * Waits for Off-canvas tray to close.
-   */
-  protected function waitForOffCanvasToClose() {
-    $this->waitForNoElement('#drupal-offcanvas');
-  }
-
-  /**
-   * Waits for an element to appear on the page.
-   *
-   * @param string $selector
-   *   CSS selector.
-   * @param int $timeout
-   *   (optional) Timeout in milliseconds, defaults to 10000.
-   */
-  protected function waitForElement($selector, $timeout = 10000) {
-    $condition = "(jQuery('$selector').length > 0)";
-    $this->assertJsCondition($condition, $timeout);
-  }
-
-  /**
-   * Gets the Off-Canvas tray element.
-   *
-   * @return \Behat\Mink\Element\NodeElement|null
-   */
-  protected function getTray() {
-    $tray = $this->getSession()->getPage()->find('css', '.ui-dialog[aria-describedby="drupal-offcanvas"]');
-    $this->assertEquals(FALSE, empty($tray), 'The tray was found.');
-    return $tray;
-  }
-
-  /**
-   * Waits for an element to be removed from the page.
-   *
-   * @param string $selector
-   *   CSS selector.
-   * @param int $timeout
-   *   (optional) Timeout in milliseconds, defaults to 10000.
-   */
-  protected function waitForNoElement($selector, $timeout = 10000) {
-    $condition = "(jQuery('$selector').length == 0)";
-    $this->assertJsCondition($condition, $timeout);
-  }
+abstract class OutsideInJavascriptTestBase extends OffCanvasTestBase {
 
   /**
    * Clicks a contextual link.
diff --git a/core/modules/outside_in/src/Tests/Ajax/OffCanvasDialogTest.php b/core/modules/system/src/Tests/Ajax/OffCanvasDialogTest.php
similarity index 72%
rename from core/modules/outside_in/src/Tests/Ajax/OffCanvasDialogTest.php
rename to core/modules/system/src/Tests/Ajax/OffCanvasDialogTest.php
index f351042..0102005 100644
--- a/core/modules/outside_in/src/Tests/Ajax/OffCanvasDialogTest.php
+++ b/core/modules/system/src/Tests/Ajax/OffCanvasDialogTest.php
@@ -1,27 +1,19 @@
 <?php
 
-namespace Drupal\outside_in\Tests\Ajax;
+namespace Drupal\system\Tests\Ajax;
 
 use Drupal\ajax_test\Controller\AjaxTestController;
 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
-use Drupal\system\Tests\Ajax\AjaxTestBase;
 
 /**
  * Performs tests on opening and manipulating dialogs via AJAX commands.
  *
- * @group outside_in
+ * @group Ajax
  */
 class OffCanvasDialogTest extends AjaxTestBase {
 
   /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = ['outside_in'];
-
-  /**
-   * Test sending AJAX requests to open and manipulate offcanvas dialog.
+   * Test sending AJAX requests to open and manipulate off-canvas dialog.
    */
   public function testDialog() {
     $this->drupalLogin($this->drupalCreateUser(['administer contact forms']));
@@ -32,9 +24,9 @@ public function testDialog() {
     $dialog_renderable = AjaxTestController::dialogContents();
     $dialog_contents = \Drupal::service('renderer')->renderRoot($dialog_renderable);
 
-    $offcanvas_expected_response = [
+    $off_canvas_expected_response = [
       'command' => 'openDialog',
-      'selector' => '#drupal-offcanvas',
+      'selector' => '#drupal-off-canvas',
       'settings' => NULL,
       'data' => $dialog_contents,
       'dialogOptions' =>
@@ -52,8 +44,8 @@ public function testDialog() {
     ];
 
     // Emulate going to the JS version of the page and check the JSON response.
-    $ajax_result = $this->drupalGetAjax('ajax-test/dialog-contents', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_dialog_offcanvas']]);
-    $this->assertEqual($offcanvas_expected_response, $ajax_result[3], 'Off-canvas dialog JSON response matches.');
+    $ajax_result = $this->drupalGetAjax('ajax-test/dialog-contents', ['query' => [MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_dialog.off_canvas']]);
+    $this->assertEqual($off_canvas_expected_response, $ajax_result[3], 'Off-canvas dialog JSON response matches.');
   }
 
 }
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 6581e60..d5e14ef 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -224,6 +224,9 @@ function system_theme() {
       ),
       'template' => 'entity-add-list',
     ),
+    'off_canvas_page_wrapper' => [
+      'variables' => ['children' => NULL],
+    ],
   ));
 }
 
@@ -1445,3 +1448,12 @@ function system_query_entity_reference_alter(AlterableInterface $query) {
   $handler = $query->getMetadata('entity_reference_selection_handler');
   $handler->entityQueryAlter($query);
 }
+
+/**
+ * Implements hook_element_info_alter().
+ */
+function system_element_info_alter(&$type) {
+  if (isset($type['page'])) {
+    $type['page']['#theme_wrappers']['off_canvas_page_wrapper'] = ['#weight' => -1000];
+  }
+}
diff --git a/core/modules/outside_in/templates/outside-in-page-wrapper.html.twig b/core/modules/system/templates/off-canvas-page-wrapper.html.twig
similarity index 80%
rename from core/modules/outside_in/templates/outside-in-page-wrapper.html.twig
rename to core/modules/system/templates/off-canvas-page-wrapper.html.twig
index adedc6b..c2f5fb9 100644
--- a/core/modules/outside_in/templates/outside-in-page-wrapper.html.twig
+++ b/core/modules/system/templates/off-canvas-page-wrapper.html.twig
@@ -12,7 +12,7 @@
  */
 #}
 {% if children %}
-  <div class="dialog-offcanvas__main-canvas" data-offcanvas-main-canvas >
+  <div class="dialog-off-canvas__main-canvas" data-off-canvas-main-canvas >
     {{ children }}
   </div>
 {% endif %}
diff --git a/core/modules/system/tests/modules/dialog_renderer_test/dialog_renderer_test.info.yml b/core/modules/system/tests/modules/dialog_renderer_test/dialog_renderer_test.info.yml
new file mode 100644
index 0000000..f7544e7
--- /dev/null
+++ b/core/modules/system/tests/modules/dialog_renderer_test/dialog_renderer_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Database Renderer Test'
+type: module
+description: 'Support module for Dialog Renderer tests.'
+core: 8.x
+package: Testing
+version: VERSION
diff --git a/core/modules/system/tests/modules/dialog_renderer_test/dialog_renderer_test.routing.yml b/core/modules/system/tests/modules/dialog_renderer_test/dialog_renderer_test.routing.yml
new file mode 100644
index 0000000..3cfbde0
--- /dev/null
+++ b/core/modules/system/tests/modules/dialog_renderer_test/dialog_renderer_test.routing.yml
@@ -0,0 +1,15 @@
+dialog_renderer_test.links:
+  path: '/dialog_renderer-test-links'
+  defaults:
+    _controller: '\Drupal\dialog_renderer_test\Controller\TestController::linksDisplay'
+    _title: 'Links'
+  requirements:
+    _access: 'TRUE'
+
+dialog_renderer_test.modal_content:
+  path: '/dialog_renderer-content'
+  defaults:
+    _controller: '\Drupal\dialog_renderer_test\Controller\TestController::modalContent'
+    _title: 'Thing 1'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/system/tests/modules/dialog_renderer_test/dialog_renderer_test.services.yml b/core/modules/system/tests/modules/dialog_renderer_test/dialog_renderer_test.services.yml
new file mode 100644
index 0000000..1bf58fa
--- /dev/null
+++ b/core/modules/system/tests/modules/dialog_renderer_test/dialog_renderer_test.services.yml
@@ -0,0 +1,6 @@
+services:
+  main_content_renderer.wide_modal:
+    class: Drupal\dialog_renderer_test\Render\MainContent\WideModalRenderer
+    arguments: ['@title_resolver', '@renderer']
+    tags:
+      - { name: render.main_content_renderer, format: drupal_modal.wide }
diff --git a/core/modules/system/tests/modules/dialog_renderer_test/src/Controller/TestController.php b/core/modules/system/tests/modules/dialog_renderer_test/src/Controller/TestController.php
new file mode 100644
index 0000000..573a656
--- /dev/null
+++ b/core/modules/system/tests/modules/dialog_renderer_test/src/Controller/TestController.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\dialog_renderer_test\Controller;
+
+use Drupal\Core\Url;
+
+/**
+ * Test controller display modal links and content.
+ */
+class TestController {
+
+  /**
+   * Return modal content.
+   *
+   * @return array
+   *   Render array for display in modal.
+   */
+  public function modalContent() {
+    return [
+      '#type' => 'markup',
+      '#markup' => 'Look at me in a modal!',
+    ];
+  }
+
+  /**
+   * Displays test links that will open in the modal dialog.
+   *
+   * @return array
+   *   Render array with links.
+   */
+  public function linksDisplay() {
+    return [
+      'normal_modal' => [
+        '#title' => 'Normal Modal!',
+        '#type' => 'link',
+        '#url' => Url::fromRoute('dialog_renderer_test.modal_content'),
+        '#attributes' => [
+          'class' => ['use-ajax'],
+          'data-dialog-type' => 'modal',
+        ],
+        '#attached' => [
+          'library' => [
+            'core/drupal.ajax',
+          ],
+        ],
+      ],
+      'wide_modal' => [
+        '#title' => 'Wide Modal!',
+        '#type' => 'link',
+        '#url' => Url::fromRoute('dialog_renderer_test.modal_content'),
+        '#attributes' => [
+          'class' => ['use-ajax'],
+          'data-dialog-type' => 'modal',
+          'data-dialog-renderer' => 'wide',
+        ],
+        '#attached' => [
+          'library' => [
+            'core/drupal.ajax',
+          ],
+        ],
+      ],
+    ];
+  }
+
+}
diff --git a/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php b/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php
new file mode 100644
index 0000000..6309f24
--- /dev/null
+++ b/core/modules/system/tests/modules/dialog_renderer_test/src/Render/MainContent/WideModalRenderer.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Drupal\dialog_renderer_test\Render\MainContent;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\OpenModalDialogCommand;
+use Drupal\Core\Render\MainContent\DialogRenderer;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Wide main content renderer for modal dialog requests.
+ *
+ * This test class is copied from \Drupal\Core\Render\MainContent\ModalRenderer
+ * to demonstrate selecting a different render via 'data-dialog-renderer' link
+ * attribute.
+ */
+class WideModalRenderer extends DialogRenderer {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function renderResponse(array $main_content, Request $request, RouteMatchInterface $route_match) {
+    $response = new AjaxResponse();
+
+    // First render the main content, because it might provide a title.
+    $content = drupal_render_root($main_content);
+
+    // Attach the library necessary for using the OpenModalDialogCommand and set
+    // the attachments for this Ajax response.
+    $main_content['#attached']['library'][] = 'core/drupal.dialog.ajax';
+    $response->setAttachments($main_content['#attached']);
+
+    // If the main content doesn't provide a title, use the title resolver.
+    $title = isset($main_content['#title']) ? $main_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject());
+
+    // Determine the title: use the title provided by the main content if any,
+    // otherwise get it from the routing information.
+    $options = $request->request->get('dialogOptions', []);
+    // Override width option.
+    $options['width'] = 700;
+
+    $response->addCommand(new OpenModalDialogCommand($title, $content, $options));
+    return $response;
+  }
+
+}
diff --git a/core/modules/outside_in/tests/modules/offcanvas_test/offcanvas_test.info.yml b/core/modules/system/tests/modules/off_canvas_test/off_canvas_test.info.yml
similarity index 91%
rename from core/modules/outside_in/tests/modules/offcanvas_test/offcanvas_test.info.yml
rename to core/modules/system/tests/modules/off_canvas_test/off_canvas_test.info.yml
index 8c6cc80..cb3dd2c 100644
--- a/core/modules/outside_in/tests/modules/offcanvas_test/offcanvas_test.info.yml
+++ b/core/modules/system/tests/modules/off_canvas_test/off_canvas_test.info.yml
@@ -6,4 +6,3 @@ version: VERSION
 core: 8.x
 dependencies:
   - block
-  - outside_in
diff --git a/core/modules/system/tests/modules/off_canvas_test/off_canvas_test.routing.yml b/core/modules/system/tests/modules/off_canvas_test/off_canvas_test.routing.yml
new file mode 100644
index 0000000..4ae56c4
--- /dev/null
+++ b/core/modules/system/tests/modules/off_canvas_test/off_canvas_test.routing.yml
@@ -0,0 +1,29 @@
+off_canvas_test.links:
+  path: '/off-canvas-test-links'
+  defaults:
+    _controller: '\Drupal\off_canvas_test\Controller\TestController::linksDisplay'
+    _title: 'Links'
+  requirements:
+    _access: 'TRUE'
+
+off_canvas_test.thing1:
+  path: '/off-canvas-thing1'
+  defaults:
+    _controller: '\Drupal\off_canvas_test\Controller\TestController::thing1'
+    _title: 'Thing 1'
+  requirements:
+    _access: 'TRUE'
+
+off_canvas_test.thing2:
+  path: '/off-canvas-thing2'
+  defaults:
+    _controller: '\Drupal\off_canvas_test\Controller\TestController::thing2'
+  requirements:
+    _access: 'TRUE'
+
+off_canvas_test.dialog_links:
+  path: '/off-canvas-dialog-links'
+  defaults:
+    _controller: '\Drupal\off_canvas_test\Controller\TestController::otherDialogLinks'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/outside_in/tests/modules/offcanvas_test/src/Controller/TestController.php b/core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php
similarity index 67%
rename from core/modules/outside_in/tests/modules/offcanvas_test/src/Controller/TestController.php
rename to core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php
index 9ee8e17..7f5a6a1 100644
--- a/core/modules/outside_in/tests/modules/offcanvas_test/src/Controller/TestController.php
+++ b/core/modules/system/tests/modules/off_canvas_test/src/Controller/TestController.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\offcanvas_test\Controller;
+namespace Drupal\off_canvas_test\Controller;
 
 use Drupal\Component\Serialization\Json;
 use Drupal\Core\Url;
@@ -37,58 +37,58 @@ public function thing2() {
   }
 
   /**
-   * Displays test links that will open in offcanvas tray.
+   * Displays test links that will open in off-canvas dialog.
    *
    * @return array
    *   Render array with links.
    */
   public function linksDisplay() {
     return [
-      'offcanvas_link_1' => [
+      'off_canvas_link_1' => [
         '#title' => 'Click Me 1!',
         '#type' => 'link',
-        '#url' => Url::fromRoute('offcanvas_test.thing1'),
+        '#url' => Url::fromRoute('off_canvas_test.thing1'),
         '#attributes' => [
           'class' => ['use-ajax'],
           'data-dialog-type' => 'dialog',
-          'data-dialog-renderer' => 'offcanvas',
+          'data-dialog-renderer' => 'off_canvas',
         ],
         '#attached' => [
           'library' => [
-            'outside_in/drupal.outside_in',
+            'core/drupal.ajax',
           ],
         ],
       ],
-      'offcanvas_link_2' => [
+      'off_canvas_link_2' => [
         '#title' => 'Click Me 2!',
         '#type' => 'link',
-        '#url' => Url::fromRoute('offcanvas_test.thing2'),
+        '#url' => Url::fromRoute('off_canvas_test.thing2'),
         '#attributes' => [
           'class' => ['use-ajax'],
           'data-dialog-type' => 'dialog',
-          'data-dialog-renderer' => 'offcanvas',
+          'data-dialog-renderer' => 'off_canvas',
           'data-dialog-options' => Json::encode([
             'width' => 555,
           ]),
         ],
         '#attached' => [
           'library' => [
-            'outside_in/drupal.outside_in',
+            'core/drupal.ajax',
           ],
         ],
       ],
       'other_dialog_links' => [
         '#title' => 'Display more links!',
         '#type' => 'link',
-        '#url' => Url::fromRoute('offcanvas_test.dialog_links'),
+        '#url' => Url::fromRoute('off_canvas_test.dialog_links'),
         '#attributes' => [
           'class' => ['use-ajax'],
           'data-dialog-type' => 'dialog',
-          'data-dialog-renderer' => 'offcanvas',
+          'data-dialog-renderer' => 'off_canvas',
         ],
         '#attached' => [
           'library' => [
-            'outside_in/drupal.outside_in',
+            'core/drupal.ajax',
           ],
         ],
       ],
@@ -96,12 +96,12 @@ public function linksDisplay() {
   }
 
   /**
-   * Displays dialogs links to be displayed inside the offcanvas tray.
+   * Displays dialogs links to be displayed inside the off-canvas dialog.
    *
-   * This links are used to test opening a modal and another offcanvas link from
-   * inside the offcanvas tray.
+   * This links are used to test opening a modal and another off-canvas link from
+   * inside the off-canvas dialog.
    *
-   * @todo Update tests to check these links work in the offcanvas tray.
+   * @todo Update tests to check these links work in the off-canvas dialog.
    *       https://www.drupal.org/node/2790073
    *
    * @return array
@@ -113,25 +113,25 @@ public function otherDialogLinks() {
       '#links' => [
         'modal_link' => [
           'title' => 'Open modal!',
-          'url' => Url::fromRoute('offcanvas_test.thing2'),
+          'url' => Url::fromRoute('off_canvas_test.thing2'),
           'attributes' => [
             'class' => ['use-ajax'],
             'data-dialog-type' => 'modal',
           ],
         ],
-        'offcanvas_link' => [
-          'title' => 'Offcanvas link!',
-          'url' => Url::fromRoute('offcanvas_test.thing2'),
+        'off_canvas_link' => [
+          'title' => 'Off-canvas link!',
+          'url' => Url::fromRoute('off_canvas_test.thing2'),
           'attributes' => [
             'class' => ['use-ajax'],
             'data-dialog-type' => 'dialog',
-            'data-dialog-renderer' => 'offcanvas',
+            'data-dialog-renderer' => 'off_canvas',
           ],
         ],
       ],
       '#attached' => [
         'library' => [
-          'outside_in/drupal.outside_in',
+          'core/drupal.ajax',
         ],
       ],
     ];
diff --git a/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php b/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php
new file mode 100644
index 0000000..ce234a1
--- /dev/null
+++ b/core/modules/system/tests/src/FunctionalJavascript/ModalRendererTest.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\Tests\system\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+
+/**
+ * Tests that dialog links use different renderer services.
+ *
+ * @group system
+ */
+class ModalRendererTest extends JavascriptTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['system', 'dialog_renderer_test'];
+
+  /**
+   * Tests that links respect 'data-dialog-renderer' attribute.
+   */
+  public function testModalRenderer() {
+    $session_assert = $this->assertSession();
+    $this->drupalGet('/dialog_renderer-test-links');
+    $this->clickLink('Normal Modal!');
+    $session_assert->assertWaitOnAjaxRequest();
+    $session_assert->elementAttributeNotContains('css', '.ui-dialog', 'style', 'width: 700px;');
+    $this->drupalGet('/dialog_renderer-test-links');
+    $this->clickLink('Wide Modal!');
+    $session_assert->assertWaitOnAjaxRequest();
+    $session_assert->elementAttributeContains('css', '.ui-dialog', 'style', 'width: 700px;');
+  }
+
+}
diff --git a/core/modules/outside_in/tests/src/FunctionalJavascript/OffCanvasTest.php b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
similarity index 62%
rename from core/modules/outside_in/tests/src/FunctionalJavascript/OffCanvasTest.php
rename to core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
index b81c723..efc011b 100644
--- a/core/modules/outside_in/tests/src/FunctionalJavascript/OffCanvasTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
@@ -1,34 +1,34 @@
 <?php
 
-namespace Drupal\Tests\outside_in\FunctionalJavascript;
+namespace Drupal\Tests\system\FunctionalJavascript;
 
 /**
- * Tests the off-canvas tray functionality.
+ * Tests the off-canvas dialog functionality.
  *
- * @group outside_in
+ * @group system
  */
-class OffCanvasTest extends OutsideInJavascriptTestBase {
+class OffCanvasTest extends OffCanvasTestBase {
 
   /**
    * {@inheritdoc}
    */
-  public static $modules = ['block', 'system', 'toolbar', 'outside_in', 'offcanvas_test'];
+  public static $modules = ['block', 'system', 'toolbar', 'off_canvas_test'];
 
   /**
-   * Tests that regular non-contextual links will work with the off-canvas tray.
+   * Tests that regular non-contextual links will work with the off-canvas dialog.
    */
   public function testOffCanvasLinks() {
     $themes = ['bartik', 'stark'];
     // Test the same functionality on multiple themes.
     foreach ($themes as $theme) {
       $this->enableTheme($theme);
-      $this->drupalGet('/offcanvas-test-links');
+      $this->drupalGet('/off-canvas-test-links');
 
       $page = $this->getSession()->getPage();
       $web_assert = $this->assertSession();
 
-      // Make sure off-canvas tray is on page when first loaded.
-      $web_assert->elementNotExists('css', '#drupal-offcanvas');
+      // Make sure off-canvas dialog is on page when first loaded.
+      $web_assert->elementNotExists('css', '#drupal-off-canvas');
 
       // Check opening and closing with two separate links.
       // Make sure tray updates to new content.
@@ -40,16 +40,16 @@ public function testOffCanvasLinks() {
         $this->waitForOffCanvasToOpen();
 
         // Check that the canvas is not on the page.
-        $web_assert->elementExists('css', '#drupal-offcanvas');
+        $web_assert->elementExists('css', '#drupal-off-canvas');
         // Check that response text is on page.
         $web_assert->pageTextContains("Thing $link_index says hello");
-        $offcanvas_tray = $this->getTray();
+        $off_canvas_tray = $this->getTray();
 
         // Check that tray is visible.
-        $this->assertEquals(TRUE, $offcanvas_tray->isVisible());
-        $header_text = $offcanvas_tray->find('css', '.ui-dialog-title')->getText();
+        $this->assertEquals(TRUE, $off_canvas_tray->isVisible());
+        $header_text = $off_canvas_tray->find('css', '.ui-dialog-title')->getText();
 
-        $tray_text = $offcanvas_tray->findById('drupal-offcanvas')->getText();
+        $tray_text = $off_canvas_tray->findById('drupal-off-canvas')->getText();
         $this->assertEquals("Thing $link_index says hello", $tray_text);
 
         if ($link_index == '2') {
@@ -57,7 +57,7 @@ public function testOffCanvasLinks() {
           $web_assert->elementExists('css', '.ui-dialog-empty-title');
           $this->assertEquals('', $header_text);
 
-          $style = $page->find('css', '.ui-dialog-offcanvas')->getAttribute('style');
+          $style = $page->find('css', '.ui-dialog-off-canvas')->getAttribute('style');
           self::assertTrue(strstr($style, 'width: 555px;') !== FALSE, 'Dialog width respected.');
         }
         else {
@@ -85,21 +85,21 @@ public function testNarrowWidth() {
       $this->enableTheme($theme);
       // Testing at the wider width.
       $this->getSession()->resizeWindow($narrow_width_breakpoint + $offset, $height);
-      $this->drupalGet('/offcanvas-test-links');
-      $this->assertFalse($page->find('css', '.dialog-offcanvas__main-canvas')->hasAttribute('style'), 'Body not padded on wide page load.');
+      $this->drupalGet('/off-canvas-test-links');
+      $this->assertFalse($page->find('css', '.dialog-off-canvas__main-canvas')->hasAttribute('style'), 'Body not padded on wide page load.');
       $page->clickLink("Click Me 1!");
       $this->waitForOffCanvasToOpen();
       // Check that the main canvas is padded when page is not narrow width and
       // tray is open.
-      $web_assert->elementAttributeContains('css', '.dialog-offcanvas__main-canvas', 'style', 'padding-right');
+      $web_assert->elementAttributeContains('css', '.dialog-off-canvas__main-canvas', 'style', 'padding-right');
 
       // Testing at the narrower width.
       $this->getSession()->resizeWindow($narrow_width_breakpoint - $offset, $height);
-      $this->drupalGet('/offcanvas-test-links');
-      $this->assertFalse($page->find('css', '.dialog-offcanvas__main-canvas')->hasAttribute('style'), 'Body not padded on narrow page load.');
+      $this->drupalGet('/off-canvas-test-links');
+      $this->assertFalse($page->find('css', '.dialog-off-canvas__main-canvas')->hasAttribute('style'), 'Body not padded on narrow page load.');
       $page->clickLink("Click Me 1!");
       $this->waitForOffCanvasToOpen();
-      $this->assertFalse($page->find('css', '.dialog-offcanvas__main-canvas')->hasAttribute('style'), 'Body not padded on narrow page with tray open.');
+      $this->assertFalse($page->find('css', '.dialog-off-canvas__main-canvas')->hasAttribute('style'), 'Body not padded on narrow page with tray open.');
     }
   }
 
diff --git a/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php
new file mode 100644
index 0000000..5703402
--- /dev/null
+++ b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTestBase.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace Drupal\Tests\system\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+
+/**
+ * Base class contains common test functionality the off-canvas dialog.
+ */
+abstract class OffCanvasTestBase extends JavascriptTestBase {
+
+  /**
+   * Waits for off-canvas dialog to open.
+   */
+  protected function waitForOffCanvasToOpen() {
+    $web_assert = $this->assertSession();
+    $web_assert->assertWaitOnAjaxRequest();
+    $this->waitForElement('#drupal-off-canvas');
+  }
+
+  /**
+   * Waits for an element to appear on the page.
+   *
+   * @param string $selector
+   *   CSS selector.
+   * @param int $timeout
+   *   (optional) Timeout in milliseconds, defaults to 10000.
+   */
+  protected function waitForElement($selector, $timeout = 10000) {
+    $condition = "(jQuery('$selector').length > 0)";
+    $this->assertJsCondition($condition, $timeout);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function drupalGet($path, array $options = [], array $headers = []) {
+    $return = parent::drupalGet($path, $options, $headers);
+
+    // After the page loaded we need to additionally wait until the settings
+    // tray Ajax activity is done.
+    $this->assertSession()->assertWaitOnAjaxRequest();
+
+    return $return;
+  }
+
+  /**
+   * Enables a theme.
+   *
+   * @param string $theme
+   *   The theme.
+   */
+  public function enableTheme($theme) {
+    // Enable the theme.
+    \Drupal::service('theme_installer')->install([$theme]);
+    $theme_config = \Drupal::configFactory()->getEditable('system.theme');
+    $theme_config->set('default', $theme);
+    $theme_config->save();
+  }
+
+  /**
+   * Waits for off-canvas dialog to close.
+   */
+  protected function waitForOffCanvasToClose() {
+    $this->waitForNoElement('#drupal-off-canvas');
+  }
+
+  /**
+   * Waits for an element to be removed from the page.
+   *
+   * @param string $selector
+   *   CSS selector.
+   * @param int $timeout
+   *   (optional) Timeout in milliseconds, defaults to 10000.
+   */
+  protected function waitForNoElement($selector, $timeout = 10000) {
+    $condition = "(jQuery('$selector').length == 0)";
+    $this->assertJsCondition($condition, $timeout);
+  }
+
+  /**
+   * Gets the Off-Canvas tray element.
+   *
+   * @return \Behat\Mink\Element\NodeElement|null
+   *   The tray page element.
+   */
+  protected function getTray() {
+    $tray = $this->getSession()->getPage()->find('css', '.ui-dialog[aria-describedby="drupal-off-canvas"]');
+    $this->assertEquals(FALSE, empty($tray), 'The tray was found.');
+    return $tray;
+  }
+
+}
diff --git a/core/modules/outside_in/tests/src/Unit/Ajax/OpenOffCanvasDialogCommandTest.php b/core/modules/system/tests/src/Unit/Ajax/OpenOffCanvasDialogCommandTest.php
similarity index 76%
rename from core/modules/outside_in/tests/src/Unit/Ajax/OpenOffCanvasDialogCommandTest.php
rename to core/modules/system/tests/src/Unit/Ajax/OpenOffCanvasDialogCommandTest.php
index cd14d6d..1bed8a1 100644
--- a/core/modules/outside_in/tests/src/Unit/Ajax/OpenOffCanvasDialogCommandTest.php
+++ b/core/modules/system/tests/src/Unit/Ajax/OpenOffCanvasDialogCommandTest.php
@@ -1,13 +1,13 @@
 <?php
 
-namespace Drupal\Tests\outside_in\Unit\Ajax;
+namespace Drupal\Tests\system\Unit\Ajax;
 
-use Drupal\outside_in\Ajax\OpenOffCanvasDialogCommand;
+use Drupal\Core\Ajax\OpenOffCanvasDialogCommand;
 use Drupal\Tests\UnitTestCase;
 
 /**
- * @coversDefaultClass \Drupal\outside_in\Ajax\OpenOffCanvasDialogCommand
- * @group outside_in
+ * @coversDefaultClass \Drupal\Core\Ajax\OpenOffCanvasDialogCommand
+ * @group Ajax
  */
 class OpenOffCanvasDialogCommandTest extends UnitTestCase {
 
@@ -19,7 +19,7 @@ public function testRender() {
 
     $expected = [
       'command' => 'openDialog',
-      'selector' => '#drupal-offcanvas',
+      'selector' => '#drupal-off-canvas',
       'settings' => NULL,
       'data' => '<p>Text!</p>',
       'dialogOptions' => [
diff --git a/core/themes/stable/css/core/dialog/off-canvas.css b/core/themes/stable/css/core/dialog/off-canvas.css
new file mode 100644
index 0000000..b649dab
--- /dev/null
+++ b/core/themes/stable/css/core/dialog/off-canvas.css
@@ -0,0 +1,40 @@
+/**
+ * @file
+ * CSS for off-canvas dialog.
+ */
+
+/* Position the off-canvas dialog container outside the right of the viewport. */
+.ui-dialog-off-canvas {
+  box-sizing: border-box;
+  height: 100%;
+  overflow: visible;
+}
+
+/* Wrap the form that's inside the off-canvas dialog. */
+.ui-dialog-off-canvas .ui-dialog-content {
+  padding: 0 20px;
+  /* Prevent horizontal scrollbar. */
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+[dir="rtl"] .ui-dialog-off-canvas .ui-dialog-content {
+  text-align: right;
+}
+
+/**
+ * Force the tray to be 100% width at the same breakpoint the dialog system uses
+ * to expand dialog widths.
+ */
+@media all and (max-width: 48em) {
+  /* 768px */
+  .ui-dialog.ui-dialog-off-canvas {
+    width: 100% !important;
+  }
+
+  /* When tray is at 100% width stop the body from scrolling */
+  .js-tray-open {
+    height: 100%;
+    overflow-y: hidden;
+  }
+}
diff --git a/core/themes/stable/css/core/dialog/off-canvas.motion.css b/core/themes/stable/css/core/dialog/off-canvas.motion.css
new file mode 100644
index 0000000..db5b6ef
--- /dev/null
+++ b/core/themes/stable/css/core/dialog/off-canvas.motion.css
@@ -0,0 +1,31 @@
+/**
+ * @file
+ * Motion effects for off-canvas dialog.
+ *
+ * Motion effects are in a separate file so that they can be easily turned off
+ * to improve performance if desired.
+ *
+ * @todo Add a configuration option for browser rendering performance to disable
+ *   this file: https://www.drupal.org/node/2784443.
+ */
+
+/* Transition the off-canvas dialog container, with 2s delay to match main canvas speed. */
+.ui-dialog-off-canvas .ui-dialog-content {
+  -webkit-transition: all .7s ease 2s;
+  -moz-transition: all .7s ease 2s;
+  transition: all .7s ease 2s;
+}
+
+@media (max-width: 700px) {
+  .ui-dialog-off-canvas .ui-dialog-content {
+    -webkit-transition: all .7s ease;
+    -moz-transition: all .7s ease;
+    transition: all .7s ease;
+  }
+}
+
+.dialog-off-canvas__main-canvas {
+  -webkit-transition: all .7s ease;
+  -moz-transition: all .7s ease;
+  transition: all .7s ease;
+}
diff --git a/core/themes/stable/stable.info.yml b/core/themes/stable/stable.info.yml
index 7e585b1..3aceac6 100644
--- a/core/themes/stable/stable.info.yml
+++ b/core/themes/stable/stable.info.yml
@@ -65,6 +65,11 @@ libraries-override:
     css:
       component:
         misc/vertical-tabs.css: css/core/vertical-tabs.css
+  core/drupal.dialog.off_canvas:
+      css:
+        component:
+          misc/dialog/off-canvas.css: css/core/dialog/off-canvas.css
+          misc/dialog/off-canvas.motion.css: css/core/dialog/off-canvas.motion.css
 
   dblog/drupal.dblog:
     css:
diff --git a/core/themes/stable/templates/layout/off-canvas-page-wrapper.html.twig b/core/themes/stable/templates/layout/off-canvas-page-wrapper.html.twig
new file mode 100644
index 0000000..c2f5fb9
--- /dev/null
+++ b/core/themes/stable/templates/layout/off-canvas-page-wrapper.html.twig
@@ -0,0 +1,18 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a page wrapper.
+ *
+ * For consistent wrapping to {{ page }} render in all themes.
+ *
+ * Available variables:
+ * - children: Contains the child elements of the page.
+ *
+ * @ingroup themeable
+ */
+#}
+{% if children %}
+  <div class="dialog-off-canvas__main-canvas" data-off-canvas-main-canvas >
+    {{ children }}
+  </div>
+{% endif %}
