diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module
index d9d20b5..4543237 100644
--- a/core/modules/contextual/contextual.module
+++ b/core/modules/contextual/contextual.module
@@ -33,6 +33,7 @@ function contextual_toolbar() {
   }
 
   $tab['contextual'] = array(
+    '#type' => 'toolbar_item',
     'tab' => array(
       '#type' => 'html_tag',
       '#tag' => 'button',
@@ -43,7 +44,6 @@ function contextual_toolbar() {
         'aria-pressed' => 'false',
       ),
     ),
-    '#theme_wrappers' => array('toolbar_tab_wrapper'),
     '#wrapper_attributes' => array(
       'class' => array('hidden', 'contextual-toolbar-tab'),
     ),
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index b8b0dcb..11c0c31 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -48,19 +48,12 @@ function toolbar_permission() {
 function toolbar_theme($existing, $type, $theme, $path) {
   $items['toolbar'] = array(
     'render element' => 'element',
+    'template' => 'toolbar',
   );
   $items['toolbar_item'] = array(
     'render element' => 'element',
   );
-  $items['toolbar_tab_wrapper'] = array(
-    'render element' => 'element',
-  );
-  $items['toolbar_tray_wrapper'] = array(
-    'render element' => 'element',
-  );
-  $items['toolbar_tray_heading_wrapper'] = array(
-    'render element' => 'element',
-  );
+
   return $items;
 }
 
@@ -97,13 +90,10 @@ function toolbar_element_info() {
   );
 
   // A toolbar item is wrapped in markup for common styling.  The 'tray'
-  // property contains a renderable array. theme_toolbar_tab() is a light
-  // wrapper around the l() function. The contents of tray are rendered in
-  // theme_toolbar_tab().
+  // property contains a renderable array.
   $elements['toolbar_item'] = array(
     '#pre_render' => array('toolbar_pre_render_item'),
     '#theme' => 'toolbar_item',
-    '#theme_wrappers' => array('toolbar_tab_wrapper'),
     'tab' => array(
       '#type' => 'link',
       '#title' => NULL,
@@ -218,24 +208,54 @@ function ($object) {
 }
 
 /**
- * Returns HTML that wraps the administration toolbar.
+ * Prepares variables for administration toolbar templates.
+ *
+ * Default template: toolbar.html.twig.
  *
  * @param array $variables
  *   An associative array containing:
  *   - element: An associative array containing the properties and children of
  *     the tray. Properties used: #children, #attributes and #bar.
  */
-function theme_toolbar(&$variables) {
-  if (!empty($variables['element']['#children'])) {
-    $element = $variables['element'];
-    $trays = '';
-    foreach (element_children($element) as $key) {
-      $trays .= drupal_render($element[$key]['tray']);
+function template_preprocess_toolbar(&$variables) {
+  $element = $variables['element'];
+
+  // Prepare the toolbar attributes.
+  $variables['attributes'] = $element['#attributes'];
+  $variables['toolbar_attributes'] = new Attribute($element['#bar']['#attributes']);
+  $variables['toolbar_heading'] = $element['#bar']['#heading'];
+
+  // Prepare the trays and tabs for each toolbar item.
+  $variables['trays'] = array();
+  $variables['tabs'] = array();
+  foreach (element_children($element) as $key) {
+    // Add the tray.
+    if (isset($element[$key]['tray'])) {
+      $variables['trays'][$key] = array(
+        'links' => $element[$key]['tray'],
+        'attributes' => new Attribute($element[$key]['tray']['#wrapper_attributes']),
+      );
+      if (array_key_exists('#heading', $element[$key]['tray'])) {
+        $variables['trays'][$key]['label'] = $element[$key]['tray']['#heading'];
+      }
+      // Unset the tray so it doesn't render twice in the template.
+      unset($element[$key]['tray']);
+    }
+
+    // Pass the wrapper attributes along.
+    if (array_key_exists('#wrapper_attributes', $element[$key])) {
+      $element[$key]['#wrapper_attributes']['class'][] = 'toolbar-tab';
+      $attributes = $element[$key]['#wrapper_attributes'];
     }
-    return '<nav' . new Attribute($element['#attributes']) . '>'
-      . '<div' . new Attribute($element['#bar']['#attributes']) . '>'
-      . '<h2 class="visually-hidden">' . $element['#bar']['#heading'] . '</h2>'
-      . $element['#children'] . '</div>' . $trays . '</nav>';
+    else {
+      $attributes = array('class' => array('toolbar-tab'));
+    }
+
+    // Add the tab.
+    $variables['tabs'][$key] = array(
+      'link' => $element[$key]['tab'],
+      'attributes' => new Attribute($attributes),
+    );
   }
 }
 
@@ -287,14 +307,6 @@ function toolbar_pre_render_item($element) {
     }
     $element['tray']['#wrapper_attributes'] += $attributes;
     $element['tray']['#wrapper_attributes']['class'][] = 'toolbar-tray';
-
-    if (!isset($element['tray']['#theme_wrappers'])) {
-      $element['tray']['#theme_wrappers'] = array();
-    }
-    // Add the standard theme_wrapper for trays.
-    array_unshift($element['tray']['#theme_wrappers'], 'toolbar_tray_wrapper');
-    // Add the theme wrapper for the tray heading.
-    array_unshift($element['tray']['#theme_wrappers'], 'toolbar_tray_heading_wrapper');
   }
 
   return $element;
