diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index ce12db5..95d4ca7 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -181,6 +181,22 @@ drupal.dialog.ajax:
     - core/drupal.ajax
     - core/drupal.dialog
 
+drupal.dialog.offcanvas:
+  version: VERSION
+  js:
+    misc/dialog/dialog.offcanvas.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupal.ajax
+    - core/drupal.announce
+    - core/drupal.dialog
+    - core/drupal.dialog.ajax
+  css:
+    component:
+      misc/dialog/css/offcanvas.css: {}
+      misc/dialog/css/offcanvas.motion.css: {}
+
 drupal.displace:
   version: VERSION
   js:
diff --git a/core/core.services.yml b/core/core.services.yml
index cb93579..93b0367 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1046,6 +1046,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.offcanvas }
   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 96%
rename from core/modules/outside_in/src/Ajax/OpenOffCanvasDialogCommand.php
rename to core/lib/Drupal/Core/Ajax/OpenOffCanvasDialogCommand.php
index f6663de..403b729 100644
--- a/core/modules/outside_in/src/Ajax/OpenOffCanvasDialogCommand.php
+++ b/core/lib/Drupal/Core/Ajax/OpenOffCanvasDialogCommand.php
@@ -1,8 +1,6 @@
 <?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.
diff --git a/core/modules/outside_in/src/Render/MainContent/OffCanvasRender.php b/core/lib/Drupal/Core/Render/MainContent/OffCanvasRender.php
similarity index 89%
rename from core/modules/outside_in/src/Render/MainContent/OffCanvasRender.php
rename to core/lib/Drupal/Core/Render/MainContent/OffCanvasRender.php
index 5d24c09..d5e0a52 100644
--- a/core/modules/outside_in/src/Render/MainContent/OffCanvasRender.php
+++ b/core/lib/Drupal/Core/Render/MainContent/OffCanvasRender.php
@@ -1,13 +1,12 @@
 <?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;
 
 /**
@@ -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.offcanvas';
     $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..fa32e05 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, 'offcanvas' 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/css/offcanvas.css b/core/misc/dialog/css/offcanvas.css
new file mode 100644
index 0000000..0c2841c
--- /dev/null
+++ b/core/misc/dialog/css/offcanvas.css
@@ -0,0 +1,36 @@
+/**
+ * @file
+ * Styling for off-canvas dialog.
+ */
+
+/* Position the offcanvas tray container outside the right of the viewport. */
+.ui-dialog-offcanvas {
+    box-sizing: border-box;
+    height: 100%;
+    overflow: visible;
+}
+
+/* Shift the main canvas to the right for right-to-left languages. */
+[dir="rtl"] #main-canvas-wrapper.js-tray-open #main-canvas {
+    right: 0;
+}
+
+/* Wrap the form that's inside the 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;
+}
+
+.offcanvas-lining {
+    background: #333;
+    position: absolute;
+    top: 0;
+    height: 100%;
+    width: 100%;
+    z-index: -1;
+}
diff --git a/core/misc/dialog/css/offcanvas.motion.css b/core/misc/dialog/css/offcanvas.motion.css
new file mode 100644
index 0000000..fef966e
--- /dev/null
+++ b/core/misc/dialog/css/offcanvas.motion.css
@@ -0,0 +1,30 @@
+/**
+ * @file
+ * Motion effects for offcanvas dialog.
+ */
+
+/* Transition the offcanvas tray container, with 2s delay to match main canvas speed. */
+#offcanvas {
+    -webkit-transition: all .7s ease 2s;
+    -moz-transition: all .7s ease 2s;
+    transition: all .7s ease 2s;
+}
+#main-canvas-wrapper #main-canvas,
+#main-canvas-wrapper.js-tray-open #main-canvas {
+    -webkit-transition: all .7s ease;
+    -moz-transition: all .7s ease;
+    transition: all .7s ease;
+}
+
+@media (max-width: 700px) {
+    #offcanvas {
+        -webkit-transition: all .7s ease;
+        -moz-transition: all .7s ease;
+        transition: all .7s ease;
+    }
+    #main-canvas-wrapper.js-tray-open #offcanvas {
+        -webkit-transition: all .7s ease;
+        -moz-transition: all .7s ease;
+        transition: all .7s ease;
+    }
+}
diff --git a/core/modules/outside_in/js/offcanvas.js b/core/misc/dialog/dialog.offcanvas.js
similarity index 95%
rename from core/modules/outside_in/js/offcanvas.js
rename to core/misc/dialog/dialog.offcanvas.js
index b3c4237..d71b67a 100644
--- a/core/modules/outside_in/js/offcanvas.js
+++ b/core/misc/dialog/dialog.offcanvas.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) {
diff --git a/core/modules/outside_in/css/outside_in.module.css b/core/modules/outside_in/css/outside_in.module.css
index 9e36037..da417d6 100644
--- a/core/modules/outside_in/css/outside_in.module.css
+++ b/core/modules/outside_in/css/outside_in.module.css
@@ -3,30 +3,6 @@
  * Styling for Settings Tray module.
  */
 
-/* Position the offcanvas tray container outside the right of the viewport. */
-.ui-dialog-offcanvas {
-  box-sizing: border-box;
-  height: 100%;
-  overflow: visible;
-}
-
-/* Shift the main canvas to the right for right-to-left languages. */
-[dir="rtl"] #main-canvas-wrapper.js-tray-open #main-canvas {
-  right: 0;
-}
-
-
-/* Wrap the form that's inside the 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;
-}
-
 /*
  * Position the edit toolbar tab.
  * @todo Move changes into contextual module when Settings Tray is not
@@ -38,12 +14,3 @@
 [dir="rtl"] .toolbar .toolbar-bar .contextual-toolbar-tab.toolbar-tab {
   float: right;
 }
-
-.offcanvas-lining {
-  background: #333;
-  position: absolute;
-  top: 0;
-  height: 100%;
-  width: 100%;
-  z-index: -1;
-}
diff --git a/core/modules/outside_in/css/outside_in.motion.css b/core/modules/outside_in/css/outside_in.motion.css
index 8fbf5cb..692a358 100644
--- a/core/modules/outside_in/css/outside_in.motion.css
+++ b/core/modules/outside_in/css/outside_in.motion.css
@@ -4,25 +4,8 @@
  *
  * 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 offcanvas tray container, with 2s delay to match main canvas speed. */
-#offcanvas {
-  -webkit-transition: all .7s ease 2s;
-  -moz-transition: all .7s ease 2s;
-  transition: all .7s ease 2s;
-}
-#main-canvas-wrapper #main-canvas,
-#main-canvas-wrapper.js-tray-open #main-canvas {
-  -webkit-transition: all .7s ease;
-  -moz-transition: all .7s ease;
-  transition: all .7s ease;
-}
-
 /* Transition the edit icon in the toolbar. */
 #toolbar-bar.button.toolbar-icon.toolbar-icon.toolbar-icon-edit:before {
   -webkit-transition: all .7s ease;
@@ -47,19 +30,6 @@
   transition: all .5s ease;
 }
 
-@media (max-width: 700px) {
-  #offcanvas {
-    -webkit-transition: all .7s ease;
-    -moz-transition: all .7s ease;
-    transition: all .7s ease;
-  }
-  #main-canvas-wrapper.js-tray-open #offcanvas {
-    -webkit-transition: all .7s ease;
-    -moz-transition: all .7s ease;
-    transition: all .7s ease;
-  }
-}
-
 /* Transition the administration tray.
 #toolbar-administration,
 #toolbar-administration * {
diff --git a/core/modules/outside_in/js/outside_in.js b/core/modules/outside_in/js/outside_in.js
index a4f94aa..8a04298 100644
--- a/core/modules/outside_in/js/outside_in.js
+++ b/core/modules/outside_in/js/outside_in.js
@@ -167,25 +167,14 @@
 
       $(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 offcanvas when
-      // needed.
+      // Loop through all Ajax links and alter those that should trigger edit
+      // mode.
       Drupal.ajax.instances
         .filter(function (instance) {
           var hasElement = instance && !!instance.element;
-          var rendererOffcanvas = false;
-          var wrapperOffcanvas = false;
-          if (hasElement) {
-            rendererOffcanvas = $(instance.element).attr('data-dialog-renderer') === 'offcanvas';
-            wrapperOffcanvas = instance.options.url.indexOf('drupal_dialog_offcanvas') === -1;
-          }
-          return hasElement && rendererOffcanvas && wrapperOffcanvas;
+          return hasElement && instance.element.hasAttribute('data-outside-in-edit');
         })
         .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);
           instance.options.data.dialogOptions = {outsideInActiveEditableId: $(instance.element).parents('.outside-in-editable').attr('id')};
           instance.progress = {type: 'fullscreen'};
         });
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.module b/core/modules/outside_in/outside_in.module
index 298b994..d60b2c7 100644
--- a/core/modules/outside_in/outside_in.module
+++ b/core/modules/outside_in/outside_in.module
@@ -38,6 +38,8 @@ function outside_in_contextual_links_view_alter(&$element, $items) {
       'class' => ['use-ajax'],
       'data-dialog-type' => 'dialog',
       'data-dialog-renderer' => 'offcanvas',
+      'data-outside-in-edit' => TRUE,
+      //'data-dialog-options' =>
     ];
 
     $element['#attached']['library'][] = 'outside_in/drupal.off_canvas';
@@ -57,26 +59,6 @@ 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) {
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/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/src/Tests/Ajax/OffCanvasDialogTest.php b/core/modules/system/src/Tests/Ajax/OffCanvasDialogTest.php
similarity index 92%
rename from core/modules/outside_in/src/Tests/Ajax/OffCanvasDialogTest.php
rename to core/modules/system/src/Tests/Ajax/OffCanvasDialogTest.php
index 1e5b0d1..3c94203 100644
--- a/core/modules/outside_in/src/Tests/Ajax/OffCanvasDialogTest.php
+++ b/core/modules/system/src/Tests/Ajax/OffCanvasDialogTest.php
@@ -1,15 +1,14 @@
 <?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 {
 
@@ -52,7 +51,7 @@ 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']]);
+    $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.');
   }
 
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 01c8c5e..0512548 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -224,6 +224,9 @@ function system_theme() {
       ),
       'template' => 'entity-add-list',
     ),
+    'offcanvas_page_wrapper' => [
+      'variables' => ['children' => NULL],
+    ],
   ));
 }
 
@@ -1447,6 +1450,16 @@ function system_path_delete($path) {
 }
 
 /**
+ * Implements hook_element_info_alter().
+ */
+function system_element_info_alter(&$type) {
+  if (isset($type['page'])) {
+    $type['page']['#theme_wrappers']['offcanvas_page_wrapper'] = ['#weight' => -1000];
+  }
+}
+
+
+/**
  * Implements hook_query_TAG_alter() for entity reference selection handlers.
  */
 function system_query_entity_reference_alter(AlterableInterface $query) {
diff --git a/core/modules/outside_in/templates/outside-in-page-wrapper.html.twig b/core/modules/system/templates/offcanvas-page-wrapper.html.twig
similarity index 91%
rename from core/modules/outside_in/templates/outside-in-page-wrapper.html.twig
rename to core/modules/system/templates/offcanvas-page-wrapper.html.twig
index 1d1c50f..feaf81c 100644
--- a/core/modules/outside_in/templates/outside-in-page-wrapper.html.twig
+++ b/core/modules/system/templates/offcanvas-page-wrapper.html.twig
@@ -12,12 +12,10 @@
  */
 #}
 {% if children %}
-  {% spaceless %}
     <div id="main-canvas-wrapper">
       <div id="main-canvas">
         {{ children }}
       </div>
     </div>
     <div class="offcanvas-lining"></div>
-  {% endspaceless %}
 {% endif %}
diff --git a/core/modules/outside_in/tests/modules/offcanvas_test/offcanvas_test.info.yml b/core/modules/system/tests/modules/offcanvas_test/offcanvas_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/offcanvas_test/offcanvas_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/offcanvas_test/offcanvas_test.info.yml
@@ -6,4 +6,3 @@ version: VERSION
 core: 8.x
 dependencies:
   - block
-  - outside_in
diff --git a/core/modules/outside_in/tests/modules/offcanvas_test/offcanvas_test.routing.yml b/core/modules/system/tests/modules/offcanvas_test/offcanvas_test.routing.yml
similarity index 100%
rename from core/modules/outside_in/tests/modules/offcanvas_test/offcanvas_test.routing.yml
rename to core/modules/system/tests/modules/offcanvas_test/offcanvas_test.routing.yml
diff --git a/core/modules/outside_in/tests/modules/offcanvas_test/src/Controller/TestController.php b/core/modules/system/tests/modules/offcanvas_test/src/Controller/TestController.php
similarity index 94%
rename from core/modules/outside_in/tests/modules/offcanvas_test/src/Controller/TestController.php
rename to core/modules/system/tests/modules/offcanvas_test/src/Controller/TestController.php
index e711fc3..b0d0b95 100644
--- a/core/modules/outside_in/tests/modules/offcanvas_test/src/Controller/TestController.php
+++ b/core/modules/system/tests/modules/offcanvas_test/src/Controller/TestController.php
@@ -54,7 +54,7 @@ public function linksDisplay() {
         ],
         '#attached' => [
           'library' => [
-            'outside_in/drupal.outside_in',
+            'core/drupal.dialog.ajax',
           ],
         ],
       ],
@@ -69,7 +69,7 @@ public function linksDisplay() {
         ],
         '#attached' => [
           'library' => [
-            'outside_in/drupal.outside_in',
+            'core/drupal.dialog.ajax',
           ],
         ],
       ],
@@ -84,7 +84,7 @@ public function linksDisplay() {
         ],
         '#attached' => [
           'library' => [
-            'outside_in/drupal.outside_in',
+            'core/drupal.dialog.ajax',
           ],
         ],
       ],
@@ -127,7 +127,7 @@ public function otherDialogLinks() {
       ],
       '#attached' => [
         'library' => [
-          'outside_in/drupal.outside_in',
+          'core/drupal.dialog.ajax',
         ],
       ],
     ];
diff --git a/core/modules/outside_in/tests/src/FunctionalJavascript/OffCanvasTest.php b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
similarity index 57%
rename from core/modules/outside_in/tests/src/FunctionalJavascript/OffCanvasTest.php
rename to core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
index a2a9514..6b567ca 100644
--- a/core/modules/outside_in/tests/src/FunctionalJavascript/OffCanvasTest.php
+++ b/core/modules/system/tests/src/FunctionalJavascript/OffCanvasTest.php
@@ -1,18 +1,19 @@
 <?php
 
-namespace Drupal\Tests\outside_in\FunctionalJavascript;
+namespace Drupal\Tests\system\FunctionalJavascript;
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
 
 /**
  * Tests the off-canvas tray functionality.
  *
- * @group outside_in
+ * @group Ajax
  */
-class OffCanvasTest extends OutsideInJavascriptTestBase {
+class OffCanvasTest extends JavascriptTestBase {
 
   /**
    * {@inheritdoc}
    */
-  public static $modules = ['block', 'system', 'toolbar', 'outside_in', 'offcanvas_test'];
+  public static $modules = ['user', 'system', 'block', 'offcanvas_test'];
 
   /**
    * Tests that regular non-contextual links will work with the off-canvas tray.
@@ -66,4 +67,51 @@ public function testOffCanvasLinks() {
     }
   }
 
+  /**
+   * 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 an element to appear on the page.
+   *
+   * @param string $selector
+   *   CSS selector.
+   * @param int $timeout
+   *   (optional) Timeout in milliseconds, defaults to 1000.
+   */
+  protected function waitForElement($selector, $timeout = 1000) {
+    $condition = "(jQuery('$selector').length > 0)";
+    $this->assertJsCondition($condition, $timeout, "Selector $selector not found.");
+  }
+
+  /**
+   * 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;
+  }
+
 }
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 80%
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..5ef060f 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 {
 
diff --git a/core/themes/stable/templates/layout/offcanvas-page-wrapper.html.twig b/core/themes/stable/templates/layout/offcanvas-page-wrapper.html.twig
new file mode 100644
index 0000000..feaf81c
--- /dev/null
+++ b/core/themes/stable/templates/layout/offcanvas-page-wrapper.html.twig
@@ -0,0 +1,21 @@
+{#
+/**
+ * @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 id="main-canvas-wrapper">
+      <div id="main-canvas">
+        {{ children }}
+      </div>
+    </div>
+    <div class="offcanvas-lining"></div>
+{% endif %}
