From b9fce45b5c3a1e3d60727f2be543b64b2cf38ca3 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Wed, 25 Mar 2020 09:37:24 +0000
Subject: [PATCH] Issue #3115223 by bnjmnm, lauriii, tedbow, xjm, alexpott:
 Remove Stable as a base theme of core themes

---
 .../src/Functional/ConfigImportUITest.php     |  8 +-
 core/modules/system/system.post_update.php    | 22 +++++
 .../Update/ClassyUninstallUpdateTest.php      |  3 +-
 .../Update/StableUninstallUpdateTest.php      | 89 +++++++++++++++++++
 .../test_theme_depending_on_stable.info.yml   |  4 +
 .../demo_umami/themes/umami/css/base.css      | 20 ++++-
 .../themes/umami/js/ajax.temporary.es6.js     | 22 -----
 .../themes/umami/js/ajax.temporary.js         | 12 ---
 .../umami/js/user.theme.temporary.es6.js      | 19 ----
 .../themes/umami/js/user.theme.temporary.js   | 12 ---
 .../field/file-link.html.twig                 |  1 -
 .../demo_umami/themes/umami/umami.info.yml    | 20 +----
 .../themes/umami/umami.libraries.yml          | 20 -----
 .../demo_umami/themes/umami/umami.theme       | 20 +++++
 .../Core/Config/ConfigImporterTest.php        |  8 +-
 .../Core/Theme/ConfirmClassyCopiesTest.php    |  3 -
 .../Core/Theme/MaintenanceThemeTest.php       |  6 +-
 core/themes/bartik/bartik.info.yml            | 22 +----
 core/themes/bartik/bartik.libraries.yml       | 20 -----
 core/themes/bartik/bartik.theme               | 21 +++++
 core/themes/bartik/css/components/form.css    |  7 +-
 core/themes/bartik/js/ajax.temporary.es6.js   | 22 -----
 core/themes/bartik/js/ajax.temporary.js       | 12 ---
 .../bartik/js/user.theme.temporary.es6.js     | 19 ----
 core/themes/bartik/js/user.theme.temporary.js | 12 ---
 .../{classy => }/field/file-link.html.twig    |  1 -
 core/themes/claro/claro.info.yml              | 38 +++-----
 core/themes/claro/claro.libraries.yml         |  8 --
 core/themes/claro/claro.theme                 | 21 +++--
 .../themes/claro/css/components/tabledrag.css |  3 +-
 .../claro/css/components/tabledrag.pcss.css   |  3 +-
 .../claro/css/theme/views_ui.admin.theme.css  |  4 +-
 .../css/theme/views_ui.admin.theme.pcss.css   |  4 +-
 core/themes/claro/js/ajax.temporary.es6.js    | 22 -----
 core/themes/claro/js/ajax.temporary.js        | 12 ---
 .../content-edit/image-widget.html.twig       |  1 -
 .../claro/templates/field/file-link.html.twig |  1 -
 .../css/components/system-status-counter.css  |  7 +-
 .../css/components/system-status-report.css   |  4 +-
 core/themes/seven/js/ajax.temporary.es6.js    | 22 -----
 core/themes/seven/js/ajax.temporary.js        | 12 ---
 .../seven/js/user.theme.temporary.es6.js      | 19 ----
 core/themes/seven/js/user.theme.temporary.js  | 12 ---
 core/themes/seven/seven.info.yml              | 29 ++----
 core/themes/seven/seven.libraries.yml         | 20 -----
 core/themes/seven/seven.theme                 | 21 +++++
 .../{classy => }/field/file-link.html.twig    |  1 -
 47 files changed, 253 insertions(+), 436 deletions(-)
 create mode 100644 core/modules/system/tests/src/Functional/Update/StableUninstallUpdateTest.php
 create mode 100644 core/modules/system/tests/themes/test_theme_depending_on_stable/test_theme_depending_on_stable.info.yml
 delete mode 100644 core/profiles/demo_umami/themes/umami/js/ajax.temporary.es6.js
 delete mode 100644 core/profiles/demo_umami/themes/umami/js/ajax.temporary.js
 delete mode 100644 core/profiles/demo_umami/themes/umami/js/user.theme.temporary.es6.js
 delete mode 100644 core/profiles/demo_umami/themes/umami/js/user.theme.temporary.js
 rename core/profiles/demo_umami/themes/umami/templates/{classy => components}/field/file-link.html.twig (90%)
 delete mode 100644 core/themes/bartik/js/ajax.temporary.es6.js
 delete mode 100644 core/themes/bartik/js/ajax.temporary.js
 delete mode 100644 core/themes/bartik/js/user.theme.temporary.es6.js
 delete mode 100644 core/themes/bartik/js/user.theme.temporary.js
 rename core/themes/bartik/templates/{classy => }/field/file-link.html.twig (91%)
 delete mode 100644 core/themes/claro/js/ajax.temporary.es6.js
 delete mode 100644 core/themes/claro/js/ajax.temporary.js
 delete mode 100644 core/themes/seven/js/ajax.temporary.es6.js
 delete mode 100644 core/themes/seven/js/ajax.temporary.js
 delete mode 100644 core/themes/seven/js/user.theme.temporary.es6.js
 delete mode 100644 core/themes/seven/js/user.theme.temporary.js
 rename core/themes/seven/templates/{classy => }/field/file-link.html.twig (90%)

diff --git a/core/modules/config/tests/src/Functional/ConfigImportUITest.php b/core/modules/config/tests/src/Functional/ConfigImportUITest.php
index 3dd18fca36ba..856fc44e0531 100644
--- a/core/modules/config/tests/src/Functional/ConfigImportUITest.php
+++ b/core/modules/config/tests/src/Functional/ConfigImportUITest.php
@@ -493,7 +493,7 @@ public function testEntityBundleDelete() {
    */
   public function testExtensionValidation() {
     \Drupal::service('module_installer')->install(['node']);
-    \Drupal::service('theme_installer')->install(['bartik']);
+    \Drupal::service('theme_installer')->install(['test_subtheme']);
     $this->rebuildContainer();
 
     $sync = $this->container->get('config.storage.sync');
@@ -504,9 +504,9 @@ public function testExtensionValidation() {
     $module_data = $this->container->get('extension.list.module')->getList();
     $this->assertTrue(isset($module_data['node']->requires['text']), 'The Node module depends on the Text module.');
     // Bartik depends on Stable.
-    unset($core['theme']['stable']);
+    unset($core['theme']['test_basetheme']);
     $theme_data = \Drupal::service('theme_handler')->rebuildThemeData();
-    $this->assertTrue(isset($theme_data['bartik']->requires['stable']), 'The Bartik theme depends on the Stable theme.');
+    $this->assertTrue(isset($theme_data['test_subtheme']->requires['test_basetheme']), 'The Test Subtheme theme depends on the Test Basetheme theme.');
     // This module does not exist.
     $core['module']['does_not_exist'] = 0;
     // This theme does not exist.
@@ -516,7 +516,7 @@ public function testExtensionValidation() {
     $this->drupalPostForm('admin/config/development/configuration', [], t('Import all'));
     $this->assertText('The configuration cannot be imported because it failed validation for the following reasons:');
     $this->assertText('Unable to uninstall the Text module since the Node module is installed.');
-    $this->assertText('Unable to uninstall the Stable theme since the Bartik theme is installed.');
+    $this->assertText('Unable to uninstall the Theme test base theme theme since the Theme test subtheme theme is installed.');
     $this->assertText('Unable to install the does_not_exist module since it does not exist.');
     $this->assertText('Unable to install the does_not_exist theme since it does not exist.');
   }
diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php
index 4d0c0b732c82..4cbd93f5a948 100644
--- a/core/modules/system/system.post_update.php
+++ b/core/modules/system/system.post_update.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\ContentEntityType;
 use Drupal\Core\Entity\ContentEntityTypeInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Extension\Exception\UnknownExtensionException;
 
 /**
  * Implements hook_removed_post_updates().
@@ -125,3 +126,24 @@ function system_post_update_uninstall_classy() {
     // depending on it.
   }
 }
+
+/**
+ * Uninstall Stable if it is no longer needed.
+ *
+ * This needs to run after system_post_update_uninstall_classy(). This will be
+ * the case since getAvailableUpdateFunctions() returns an alphabetically sorted
+ * list of post_update hooks to be run.
+ *
+ * @see Drupal\Core\Update\UpdateRegistry::getAvailableUpdateFunctions()
+ */
+function system_post_update_uninstall_stable() {
+  /** @var \Drupal\Core\Extension\ThemeInstallerInterface $theme_installer */
+  $theme_installer = \Drupal::getContainer()->get('theme_installer');
+  try {
+    $theme_installer->uninstall(['stable']);
+  }
+  catch (\InvalidArgumentException | UnknownExtensionException $exception) {
+    // Exception is thrown if Stable wasn't installed or if there are themes
+    // depending on it.
+  }
+}
diff --git a/core/modules/system/tests/src/Functional/Update/ClassyUninstallUpdateTest.php b/core/modules/system/tests/src/Functional/Update/ClassyUninstallUpdateTest.php
index 81be40b8b627..a59229958dab 100644
--- a/core/modules/system/tests/src/Functional/Update/ClassyUninstallUpdateTest.php
+++ b/core/modules/system/tests/src/Functional/Update/ClassyUninstallUpdateTest.php
@@ -28,13 +28,14 @@ public function testUpdate() {
     /** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
     $theme_handler = $this->container->get('theme_handler');
     $this->assertTrue($theme_handler->themeExists('classy'));
+    $this->assertTrue($theme_handler->themeExists('seven'));
 
     $this->runUpdates();
 
     // Ensure that Classy is not installed after running updates.
     $theme_handler->refreshInfo();
     $this->assertFalse($theme_handler->themeExists('classy'));
-    $this->assertTrue($theme_handler->themeExists('stable'));
+    $this->assertTrue($theme_handler->themeExists('seven'));
   }
 
   /**
diff --git a/core/modules/system/tests/src/Functional/Update/StableUninstallUpdateTest.php b/core/modules/system/tests/src/Functional/Update/StableUninstallUpdateTest.php
new file mode 100644
index 000000000000..efe02dec785c
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Update/StableUninstallUpdateTest.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\Tests\system\Functional\Update;
+
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+
+/**
+ * Ensures that update hook uninstalls Stable when it's no longer needed.
+ *
+ * @group Update
+ * @see system_post_update_uninstall_stable()
+ */
+class StableUninstallUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../fixtures/update/drupal-8.8.0.bare.standard.php.gz',
+    ];
+  }
+
+  /**
+   * Ensures that Stable is disabled if it's no longer needed.
+   */
+  public function testUpdate() {
+    /** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
+    $theme_handler = $this->container->get('theme_handler');
+    $this->assertTrue($theme_handler->themeExists('stable'));
+    $this->assertTrue($theme_handler->themeExists('seven'));
+
+    $this->runUpdates();
+
+    // Ensure that Stable is not installed after running updates.
+    $theme_handler->refreshInfo();
+    $this->assertFalse($theme_handler->themeExists('stable'));
+    $this->assertTrue($theme_handler->themeExists('seven'));
+  }
+
+  /**
+   * Ensures that updates run without errors when Stable is not installed.
+   */
+  public function testUpdateStableNotInstalled() {
+    /** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
+    $theme_handler = $this->container->get('theme_handler');
+    $theme_list = array_keys($theme_handler->listInfo());
+    /** @var \Drupal\Core\Extension\ThemeInstallerInterface $theme_installer */
+    $theme_installer = $this->container->get('theme_installer');
+    $theme_installer->install(['stark']);
+    $this->container->get('config.factory')
+      ->getEditable('system.theme')
+      ->set('default', 'stark')
+      ->set('admin', '')
+      ->save();
+    $theme_handler->refreshInfo();
+
+    // Uninstall all themes that were installed prior to enabling Stark.
+    $theme_installer->uninstall($theme_list);
+
+    // Ensure that Stable is not installed anymore.
+    $theme_handler->refreshInfo();
+    $this->assertFalse($theme_handler->themeExists('stable'));
+
+    $this->runUpdates();
+
+    $theme_handler->refreshInfo();
+    $this->assertFalse($theme_handler->themeExists('stable'));
+  }
+
+  /**
+   * Ensures that updates run without errors when Stable is still needed.
+   */
+  public function testUpdateStableNeeded() {
+    /** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
+    $theme_handler = $this->container->get('theme_handler');
+    /** @var \Drupal\Core\Extension\ThemeInstallerInterface $theme_installer */
+    $theme_installer = $this->container->get('theme_installer');
+    $theme_installer->install(['test_theme_depending_on_stable']);
+    $this->assertTrue($theme_handler->themeExists('stable'));
+
+    $this->runUpdates();
+
+    // Ensure that Stable is still installed after running tests.
+    $theme_handler->refreshInfo();
+    $this->assertTrue($theme_handler->themeExists('stable'));
+  }
+
+}
diff --git a/core/modules/system/tests/themes/test_theme_depending_on_stable/test_theme_depending_on_stable.info.yml b/core/modules/system/tests/themes/test_theme_depending_on_stable/test_theme_depending_on_stable.info.yml
new file mode 100644
index 000000000000..a1624b5c572c
--- /dev/null
+++ b/core/modules/system/tests/themes/test_theme_depending_on_stable/test_theme_depending_on_stable.info.yml
@@ -0,0 +1,4 @@
+type: theme
+base theme: stable
+name: 'Test theme depending on Stable'
+version: VERSION
diff --git a/core/profiles/demo_umami/themes/umami/css/base.css b/core/profiles/demo_umami/themes/umami/css/base.css
index 63532a340f5e..f0d02288cda3 100644
--- a/core/profiles/demo_umami/themes/umami/css/base.css
+++ b/core/profiles/demo_umami/themes/umami/css/base.css
@@ -275,20 +275,21 @@ fieldset {
  * normalize.css 7.0.0.
  */
 button,
-input:not([type="file"]),
 textarea {
   line-height: 1.5rem;
 }
-optgroup {
+optgroup,
+input:not([type="file"]) {
   line-height: normal;
 }
 
 /**
- * Prevent regression due to -webkit-appearance being set to button in
- * normalize.css 4.1.0.
+ * Prevent regression due to changes in normalize.css 4.1.0.
  */
 ::-webkit-file-upload-button {
   -webkit-appearance: push-button;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 1em;
 }
 
 ul,
@@ -306,3 +307,14 @@ ul ol {
 p {
   margin-bottom: 1.28rem;
 }
+
+/**
+ * Prevent regression table/td/th rules removed in normalize.css 7.0.0.
+ */
+table {
+  border-collapse: collapse;
+}
+td,
+th {
+  padding: 0;
+}
diff --git a/core/profiles/demo_umami/themes/umami/js/ajax.temporary.es6.js b/core/profiles/demo_umami/themes/umami/js/ajax.temporary.es6.js
deleted file mode 100644
index faf0e1c527b3..000000000000
--- a/core/profiles/demo_umami/themes/umami/js/ajax.temporary.es6.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * @file
- * Provides theme function for Ajax-related markup.
- *
- * This file is a temporary addition to the theme to override
- * stable/drupal.ajax. This is to help surface potential issues that may arise
- * once the theme no longer depends on stable/drupal.ajax.
- * @todo delete this file in https://drupal.org/node/3111468
- */
-
-(($, Drupal) => {
-  /**
-   * Provide a wrapper for the AJAX progress bar element.
-   *
-   * @param {jQuery} $element
-   *   Progress bar element.
-   * @return {string}
-   *   The HTML markup for the progress bar.
-   */
-  Drupal.theme.ajaxProgressBar = $element =>
-    $('<div class="ajax-progress ajax-progress-bar"></div>').append($element);
-})(jQuery, Drupal);
diff --git a/core/profiles/demo_umami/themes/umami/js/ajax.temporary.js b/core/profiles/demo_umami/themes/umami/js/ajax.temporary.js
deleted file mode 100644
index 4c1cd8e6ce15..000000000000
--- a/core/profiles/demo_umami/themes/umami/js/ajax.temporary.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal) {
-  Drupal.theme.ajaxProgressBar = function ($element) {
-    return $('<div class="ajax-progress ajax-progress-bar"></div>').append($element);
-  };
-})(jQuery, Drupal);
\ No newline at end of file
diff --git a/core/profiles/demo_umami/themes/umami/js/user.theme.temporary.es6.js b/core/profiles/demo_umami/themes/umami/js/user.theme.temporary.es6.js
deleted file mode 100644
index 461103967de0..000000000000
--- a/core/profiles/demo_umami/themes/umami/js/user.theme.temporary.es6.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @file
- * Theme elements for user password forms.
- *
- * This file is a temporary addition to the theme to override
- * stable/drupal.user. This is to help surface potential issues that may arise
- * once the theme no longer depends on stable/drupal.user.
- */
-
-(Drupal => {
-  /**
-   * Constructs a password confirm message element
-   *
-   * @return {string}
-   *   A string representing a DOM fragment.
-   */
-  Drupal.theme.passwordConfirmMessage = translate =>
-    `<div aria-live="polite" aria-atomic="true" class="password-confirm-message js-password-confirm-message">${translate.confirmTitle} <span></span></div>`;
-})(Drupal);
diff --git a/core/profiles/demo_umami/themes/umami/js/user.theme.temporary.js b/core/profiles/demo_umami/themes/umami/js/user.theme.temporary.js
deleted file mode 100644
index 4e00e623192a..000000000000
--- a/core/profiles/demo_umami/themes/umami/js/user.theme.temporary.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (Drupal) {
-  Drupal.theme.passwordConfirmMessage = function (translate) {
-    return "<div aria-live=\"polite\" aria-atomic=\"true\" class=\"password-confirm-message js-password-confirm-message\">".concat(translate.confirmTitle, " <span></span></div>");
-  };
-})(Drupal);
\ No newline at end of file
diff --git a/core/profiles/demo_umami/themes/umami/templates/classy/field/file-link.html.twig b/core/profiles/demo_umami/themes/umami/templates/components/field/file-link.html.twig
similarity index 90%
rename from core/profiles/demo_umami/themes/umami/templates/classy/field/file-link.html.twig
rename to core/profiles/demo_umami/themes/umami/templates/components/field/file-link.html.twig
index a54df5cdb0e2..34933aa7f7e5 100644
--- a/core/profiles/demo_umami/themes/umami/templates/classy/field/file-link.html.twig
+++ b/core/profiles/demo_umami/themes/umami/templates/components/field/file-link.html.twig
@@ -10,7 +10,6 @@
  * - file_size: The size of the file.
  *
  * @see template_preprocess_file_link()
- * @see stable_preprocess_image_widget()
  */
 #}
 {{ attach_library('umami/classy.file') }}
diff --git a/core/profiles/demo_umami/themes/umami/umami.info.yml b/core/profiles/demo_umami/themes/umami/umami.info.yml
index e37c4484adbf..28b4f51b0c94 100644
--- a/core/profiles/demo_umami/themes/umami/umami.info.yml
+++ b/core/profiles/demo_umami/themes/umami/umami.info.yml
@@ -1,6 +1,6 @@
 name: Umami
 type: theme
-base theme: stable
+base theme: false
 description: 'The theme used for the Umami food magazine demonstration site.'
 version: VERSION
 libraries:
@@ -25,27 +25,9 @@ libraries-override:
       theme:
         layouts/fourcol_section/fourcol_section.css: layouts/fourcol_section/fourcol_section.css
 
-  # @todo remove this library override in https://drupal.org/node/3115223
-  filter/drupal.filter.admin:
-    css:
-      theme:
-        /core/themes/stable/css/filter/filter.admin.css: false
-  # @todo remove this library override in https://drupal.org/node/3115223
-  filter/drupal.filter:
-    css:
-      theme:
-        /core/themes/stable/css/filter/filter.admin.css: false
-  # @todo remove this library override in https://drupal.org/node/3111468
-  stable/drupal.ajax: umami/empty
-  # @todo remove this library override in https://drupal.org/node/3111468
-  stable/drupal.user: umami/empty
-
 libraries-extend:
   tour/tour-styling:
     - umami/demo-umami-tour
-  # @todo remove this library extend in https://drupal.org/node/3111468
-  core/drupal.ajax:
-    - umami/temporary.ajax
   core/drupal.dialog:
     - umami/classy.dialog
   core/drupal.dropbutton:
diff --git a/core/profiles/demo_umami/themes/umami/umami.libraries.yml b/core/profiles/demo_umami/themes/umami/umami.libraries.yml
index 2e0a0071929c..c5faca71c55e 100644
--- a/core/profiles/demo_umami/themes/umami/umami.libraries.yml
+++ b/core/profiles/demo_umami/themes/umami/umami.libraries.yml
@@ -1,12 +1,3 @@
-# Drupal throws an IncompleteLibraryDefinitionException if a base theme defined
-# library is set to false in a subtheme's libraries-override. This empty library
-# is used as a workaround.
-# @see https://www.drupal.org/node/3098375
-#  @todo remove this in https://drupal.org/node/3111468
-empty:
-  version: VERSION
-  css: {}
-
 global:
   version: VERSION
   css:
@@ -91,9 +82,6 @@ user:
   css:
     component:
       css/components/user/user.css: { weight: -10 }
-  js:
-    # @todo remove this file from library in https://drupal.org/node/3111468
-    js/user.theme.temporary.js: {}
 
 webfonts-open-sans:
   remote: https://fonts.google.com
@@ -254,11 +242,3 @@ classy.search-results:
   css:
     component:
       css/classy/components/search-results.css: {}
-
-# @todo remove library in https://drupal.org/node/3111468
-temporary.ajax:
-  version: VERSION
-  js:
-    js/ajax.temporary.js: {}
-  dependencies:
-    - core/jquery
diff --git a/core/profiles/demo_umami/themes/umami/umami.theme b/core/profiles/demo_umami/themes/umami/umami.theme
index 524f0155335a..5507d3315b21 100644
--- a/core/profiles/demo_umami/themes/umami/umami.theme
+++ b/core/profiles/demo_umami/themes/umami/umami.theme
@@ -152,3 +152,23 @@ function umami_preprocess_image_widget(&$variables) {
     $variables['data']["file_{$file->id()}"]['filename']['#suffix'] = ' <span class="file-size">(' . format_size($file->getSize()) . ')</span> ';
   }
 }
+
+/**
+ * Implements template_preprocess_links().
+ *
+ * This makes it so array keys of #links items are added as a class. This
+ * functionality was removed in Drupal 8.1, but still neccessary in some
+ * instances.
+ *
+ * @todo remove in https://drupal.org/node/3120962
+ */
+function umami_preprocess_links(&$variables) {
+  if (!empty($variables['links'])) {
+    foreach ($variables['links'] as $key => $value) {
+      if (!is_numeric($key)) {
+        $class = Html::getClass($key);
+        $variables['links'][$key]['attributes']->addClass($class);
+      }
+    }
+  }
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php
index db38a87af501..c2b6da141463 100644
--- a/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php
@@ -584,7 +584,7 @@ public function testUnmetDependency() {
     $extensions['theme']['unknown_theme'] = 0;
     // Add a module and a theme that depend on uninstalled extensions.
     $extensions['module']['book'] = 0;
-    $extensions['theme']['bartik'] = 0;
+    $extensions['theme']['test_subtheme'] = 0;
 
     $sync->write('core.extension', $extensions);
     try {
@@ -597,7 +597,7 @@ public function testUnmetDependency() {
         'Unable to install the <em class="placeholder">unknown_module</em> module since it does not exist.',
         'Unable to install the <em class="placeholder">Book</em> module since it requires the <em class="placeholder">Node, Text, Field, Filter, User</em> modules.',
         'Unable to install the <em class="placeholder">unknown_theme</em> theme since it does not exist.',
-        'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Stable</em> theme.',
+        'Unable to install the <em class="placeholder">Theme test subtheme</em> theme since it requires the <em class="placeholder">Theme test base theme</em> theme.',
         'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on the <em class="placeholder">unknown</em> configuration that will not exist after import.',
         'Configuration <em class="placeholder">config_test.dynamic.dotted.existing</em> depends on the <em class="placeholder">config_test.dynamic.dotted.deleted</em> configuration that will not exist after import.',
         'Configuration <em class="placeholder">config_test.dynamic.dotted.module</em> depends on the <em class="placeholder">unknown</em> module that will not be installed after import.',
@@ -639,7 +639,7 @@ public function testUnmetDependency() {
         'Unable to install the <em class="placeholder">unknown_module</em> module since it does not exist.',
         'Unable to install the <em class="placeholder">Book</em> module since it requires the <em class="placeholder">Node, Text, Field, Filter, User</em> modules.',
         'Unable to install the <em class="placeholder">unknown_theme</em> theme since it does not exist.',
-        'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Stable</em> theme.',
+        'Unable to install the <em class="placeholder">Theme test subtheme</em> theme since it requires the <em class="placeholder">Theme test base theme</em> theme.',
         'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on the <em class="placeholder">unknown</em> configuration that will not exist after import.',
         'Configuration <em class="placeholder">config_test.dynamic.dotted.existing</em> depends on the <em class="placeholder">config_test.dynamic.dotted.deleted</em> configuration that will not exist after import.',
         'Configuration <em class="placeholder">config_test.dynamic.dotted.module</em> depends on the <em class="placeholder">unknown</em> module that will not be installed after import.',
@@ -648,7 +648,7 @@ public function testUnmetDependency() {
         'Unable to install the <em class="placeholder">unknown_module</em> module since it does not exist.',
         'Unable to install the <em class="placeholder">Book</em> module since it requires the <em class="placeholder">Node, Text, Field, Filter, User</em> modules.',
         'Unable to install the <em class="placeholder">unknown_theme</em> theme since it does not exist.',
-        'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Stable</em> theme.',
+        'Unable to install the <em class="placeholder">Theme test subtheme</em> theme since it requires the <em class="placeholder">Theme test base theme</em> theme.',
         'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on configuration (<em class="placeholder">unknown, unknown2</em>) that will not exist after import.',
         'Configuration <em class="placeholder">config_test.dynamic.dotted.existing</em> depends on the <em class="placeholder">config_test.dynamic.dotted.deleted</em> configuration that will not exist after import.',
         'Configuration <em class="placeholder">config_test.dynamic.dotted.module</em> depends on modules (<em class="placeholder">unknown, Database Logging</em>) that will not be installed after import.',
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/ConfirmClassyCopiesTest.php b/core/tests/Drupal/KernelTests/Core/Theme/ConfirmClassyCopiesTest.php
index 3268e1f3a930..68ca73be2260 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/ConfirmClassyCopiesTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/ConfirmClassyCopiesTest.php
@@ -226,7 +226,6 @@ public function providerTestClassyCopies() {
             'field--comment.html.twig',
             'link-formatter-link-separate.html.twig',
             'field.html.twig',
-            'file-link.html.twig',
             'field--text-with-summary.html.twig',
             'field--node--uid.html.twig',
             'field--node--title.html.twig',
@@ -488,7 +487,6 @@ public function providerTestClassyCopies() {
             'field--comment.html.twig',
             'link-formatter-link-separate.html.twig',
             'field.html.twig',
-            'file-link.html.twig',
             'field--text-with-summary.html.twig',
             'field--node--uid.html.twig',
             'field--node--title.html.twig',
@@ -636,7 +634,6 @@ public function providerTestClassyCopies() {
             'field--comment.html.twig',
             'link-formatter-link-separate.html.twig',
             'field.html.twig',
-            'file-link.html.twig',
             'field--text-with-summary.html.twig',
             'field--node--uid.html.twig',
             'field--node--title.html.twig',
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/MaintenanceThemeTest.php b/core/tests/Drupal/KernelTests/Core/Theme/MaintenanceThemeTest.php
index 8f09bcd5a4e5..3c1dcef5951d 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/MaintenanceThemeTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/MaintenanceThemeTest.php
@@ -15,7 +15,7 @@ class MaintenanceThemeTest extends KernelTestBase {
    * Tests that the maintenance theme initializes the theme and its base themes.
    */
   public function testMaintenanceTheme() {
-    $this->setSetting('maintenance_theme', 'seven');
+    $this->setSetting('maintenance_theme', 'test_subtheme');
     // Get the maintenance theme loaded.
     drupal_maintenance_theme();
 
@@ -23,11 +23,11 @@ public function testMaintenanceTheme() {
     $this->assertTrue(\Drupal::theme()->hasActiveTheme());
 
     $active_theme = \Drupal::theme()->getActiveTheme();
-    $this->assertEquals('seven', $active_theme->getName());
+    $this->assertEquals('test_subtheme', $active_theme->getName());
 
     $base_themes = $active_theme->getBaseThemeExtensions();
     $base_theme_names = array_keys($base_themes);
-    $this->assertSame(['stable'], $base_theme_names);
+    $this->assertSame(['test_basetheme'], $base_theme_names);
   }
 
 }
diff --git a/core/themes/bartik/bartik.info.yml b/core/themes/bartik/bartik.info.yml
index a1e1f546402e..7a184637bbb3 100644
--- a/core/themes/bartik/bartik.info.yml
+++ b/core/themes/bartik/bartik.info.yml
@@ -13,7 +13,7 @@
 # changes.
 name: Bartik
 type: theme
-base theme: stable
+base theme: false
 description: 'A flexible, recolorable theme with many regions and a responsive, mobile-first layout.'
 package: Core
 version: VERSION
@@ -23,27 +23,7 @@ libraries:
   - core/normalize
   - bartik/global-styling
 
-libraries-override:
-  # @todo remove this library override in https://drupal.org/node/3115223
-  filter/drupal.filter.admin:
-    css:
-      theme:
-        /core/themes/stable/css/filter/filter.admin.css: false
-
-  # @todo remove this library override in https://drupal.org/node/3115223
-  filter/drupal.filter:
-    css:
-      theme:
-        /core/themes/stable/css/filter/filter.admin.css: false
-  # @todo remove this library override in https://drupal.org/node/3111468
-  stable/drupal.ajax: bartik/empty
-  # @todo remove this library override in https://drupal.org/node/3111468
-  stable/drupal.user: bartik/empty
-
 libraries-extend:
-  # @todo remove this library extend in https://drupal.org/node/3111468
-#  core/drupal.ajax:
-#    - bartik/temporary.ajax
   core/drupal.dialog:
     - bartik/classy.dialog
   core/drupal.dropbutton:
diff --git a/core/themes/bartik/bartik.libraries.yml b/core/themes/bartik/bartik.libraries.yml
index 62e9cb322028..820687e00125 100644
--- a/core/themes/bartik/bartik.libraries.yml
+++ b/core/themes/bartik/bartik.libraries.yml
@@ -1,12 +1,3 @@
-# Drupal throws an IncompleteLibraryDefinitionException if a base theme defined
-# library is set to false in a subtheme's libraries-override. This empty library
-# is used as a workaround.
-# @see https://www.drupal.org/node/3098375
-#  @todo remove this in https://drupal.org/node/3111468
-empty:
-  version: VERSION
-  css: {}
-
 global-styling:
   version: VERSION
   css:
@@ -129,9 +120,6 @@ user:
   css:
     component:
       css/components/user.css: { weight: -10 }
-  js:
-    # @todo remove this file from library in https://drupal.org/node/3111468
-    js/user.theme.temporary.js: {}
 
 filter:
   version: VERSION
@@ -209,11 +197,3 @@ classy.search-results:
   css:
     component:
       css/classy/components/search-results.css: {}
-
-# @todo remove library in https://drupal.org/node/3111468
-temporary.ajax:
-  version: VERSION
-  js:
-    js/ajax.temporary.js: {}
-  dependencies:
-    - core/jquery
diff --git a/core/themes/bartik/bartik.theme b/core/themes/bartik/bartik.theme
index 22b3924493cc..1bbd831109b3 100644
--- a/core/themes/bartik/bartik.theme
+++ b/core/themes/bartik/bartik.theme
@@ -5,6 +5,7 @@
  * Functions to support theming in the Bartik theme.
  */
 
+use Drupal\Component\Utility\Html;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Template\Attribute;
 use Drupal\views\Form\ViewsForm;
@@ -177,3 +178,23 @@ function bartik_preprocess_image_widget(&$variables) {
     $variables['data']["file_{$file->id()}"]['filename']['#suffix'] = ' <span class="file-size">(' . format_size($file->getSize()) . ')</span> ';
   }
 }
+
+/**
+ * Implements template_preprocess_links().
+ *
+ * This makes it so array keys of #links items are added as a class. This
+ * functionality was removed in Drupal 8.1, but still neccessary in some
+ * instances.
+ *
+ * @todo remove in https://drupal.org/node/3120962
+ */
+function bartik_preprocess_links(&$variables) {
+  if (!empty($variables['links'])) {
+    foreach ($variables['links'] as $key => $value) {
+      if (!is_numeric($key)) {
+        $class = Html::getClass($key);
+        $variables['links'][$key]['attributes']->addClass($class);
+      }
+    }
+  }
+}
diff --git a/core/themes/bartik/css/components/form.css b/core/themes/bartik/css/components/form.css
index d708064a47b6..9fbdc6a1bdf4 100644
--- a/core/themes/bartik/css/components/form.css
+++ b/core/themes/bartik/css/components/form.css
@@ -20,7 +20,7 @@ form {
 button {
   line-height: 1.21875rem;
 }
-input:not([type="file"]) {
+input {
   line-height: normal;
 }
 select {
@@ -96,11 +96,12 @@ textarea {
 }
 
 /**
- * Prevent regression due to -webkit-appearance being set to button in
- * normalize.css 4.1.0.
+ * Prevent regression due to -webkit-appearance being set to button and
+ * font-family being set to inherit normalize.css 4.1.0.
  */
 ::-webkit-file-upload-button {
   -webkit-appearance: push-button;
+  font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, sans-serif;
 }
 
 /**
diff --git a/core/themes/bartik/js/ajax.temporary.es6.js b/core/themes/bartik/js/ajax.temporary.es6.js
deleted file mode 100644
index faf0e1c527b3..000000000000
--- a/core/themes/bartik/js/ajax.temporary.es6.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * @file
- * Provides theme function for Ajax-related markup.
- *
- * This file is a temporary addition to the theme to override
- * stable/drupal.ajax. This is to help surface potential issues that may arise
- * once the theme no longer depends on stable/drupal.ajax.
- * @todo delete this file in https://drupal.org/node/3111468
- */
-
-(($, Drupal) => {
-  /**
-   * Provide a wrapper for the AJAX progress bar element.
-   *
-   * @param {jQuery} $element
-   *   Progress bar element.
-   * @return {string}
-   *   The HTML markup for the progress bar.
-   */
-  Drupal.theme.ajaxProgressBar = $element =>
-    $('<div class="ajax-progress ajax-progress-bar"></div>').append($element);
-})(jQuery, Drupal);
diff --git a/core/themes/bartik/js/ajax.temporary.js b/core/themes/bartik/js/ajax.temporary.js
deleted file mode 100644
index 4c1cd8e6ce15..000000000000
--- a/core/themes/bartik/js/ajax.temporary.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal) {
-  Drupal.theme.ajaxProgressBar = function ($element) {
-    return $('<div class="ajax-progress ajax-progress-bar"></div>').append($element);
-  };
-})(jQuery, Drupal);
\ No newline at end of file
diff --git a/core/themes/bartik/js/user.theme.temporary.es6.js b/core/themes/bartik/js/user.theme.temporary.es6.js
deleted file mode 100644
index 461103967de0..000000000000
--- a/core/themes/bartik/js/user.theme.temporary.es6.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @file
- * Theme elements for user password forms.
- *
- * This file is a temporary addition to the theme to override
- * stable/drupal.user. This is to help surface potential issues that may arise
- * once the theme no longer depends on stable/drupal.user.
- */
-
-(Drupal => {
-  /**
-   * Constructs a password confirm message element
-   *
-   * @return {string}
-   *   A string representing a DOM fragment.
-   */
-  Drupal.theme.passwordConfirmMessage = translate =>
-    `<div aria-live="polite" aria-atomic="true" class="password-confirm-message js-password-confirm-message">${translate.confirmTitle} <span></span></div>`;
-})(Drupal);
diff --git a/core/themes/bartik/js/user.theme.temporary.js b/core/themes/bartik/js/user.theme.temporary.js
deleted file mode 100644
index 4e00e623192a..000000000000
--- a/core/themes/bartik/js/user.theme.temporary.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (Drupal) {
-  Drupal.theme.passwordConfirmMessage = function (translate) {
-    return "<div aria-live=\"polite\" aria-atomic=\"true\" class=\"password-confirm-message js-password-confirm-message\">".concat(translate.confirmTitle, " <span></span></div>");
-  };
-})(Drupal);
\ No newline at end of file
diff --git a/core/themes/bartik/templates/classy/field/file-link.html.twig b/core/themes/bartik/templates/field/file-link.html.twig
similarity index 91%
rename from core/themes/bartik/templates/classy/field/file-link.html.twig
rename to core/themes/bartik/templates/field/file-link.html.twig
index 06f6f054fb75..53f49c4ddebe 100644
--- a/core/themes/bartik/templates/classy/field/file-link.html.twig
+++ b/core/themes/bartik/templates/field/file-link.html.twig
@@ -10,7 +10,6 @@
  * - file_size: The size of the file.
  *
  * @see template_preprocess_file_link()
- * @see stable_preprocess_image_widget()
  */
 #}
 {{ attach_library('bartik/classy.file') }}
diff --git a/core/themes/claro/claro.info.yml b/core/themes/claro/claro.info.yml
index b56bcc51de25..d04bf1df4d62 100644
--- a/core/themes/claro/claro.info.yml
+++ b/core/themes/claro/claro.info.yml
@@ -12,7 +12,7 @@
 # and understand that Claro could break your modifications as it changes.
 name: Claro
 type: theme
-base theme: stable
+base theme: false
 description: 'A clean, accessible, and powerful Drupal administration theme.'
 alt text: 'Screenshot of Claro, Drupal administration theme.'
 package: Core
@@ -26,22 +26,22 @@ libraries-override:
   system/base:
     css:
       component:
-        /core/themes/stable/css/system/components/ajax-progress.module.css: css/components/ajax-progress.module.css
-        /core/themes/stable/css/system/components/autocomplete-loading.module.css: css/components/autocomplete-loading.module.css
-        /core/themes/stable/css/system/components/system-status-counter.css: css/components/system-status-counter.css
-        /core/themes/stable/css/system/components/system-status-report-counters.css: css/components/system-status-report-counters.css
-        /core/themes/stable/css/system/components/system-status-report-general-info.css: css/components/system-status-report-general-info.css
-        /core/themes/stable/css/system/components/tabledrag.module.css: css/components/tabledrag.css
+        css/components/ajax-progress.module.css: css/components/ajax-progress.module.css
+        css/components/autocomplete-loading.module.css: css/components/autocomplete-loading.module.css
+        css/components/system-status-counter.css: css/components/system-status-counter.css
+        css/components/system-status-report-counters.css: css/components/system-status-report-counters.css
+        css/components/system-status-report-general-info.css: css/components/system-status-report-general-info.css
+        css/components/tabledrag.module.css: css/components/tabledrag.css
 
   system/admin:
     css:
       theme:
-        /core/themes/stable/css/system/system.admin.css: false
+        css/system.admin.css: false
 
   core/drupal.dropbutton:
     css:
       component:
-        /core/themes/stable/css/core/dropbutton/dropbutton.css: css/components/dropbutton.css
+        misc/dropbutton/dropbutton.css: css/components/dropbutton.css
 
   core/drupal.tabledrag:
     js:
@@ -50,7 +50,7 @@ libraries-override:
   core/drupal.vertical-tabs:
     css:
       component:
-        /core/themes/stable/css/core/vertical-tabs.css: false
+        misc/vertical-tabs.css: false
     js:
       misc/vertical-tabs.js: js/vertical-tabs.js
 
@@ -69,24 +69,12 @@ libraries-override:
   field_ui/drupal.field_ui:
     css:
       theme:
-        /core/themes/stable/css/field_ui/field_ui.admin.css: css/theme/field-ui.admin.css
-
-  # @todo remove this library override in https://drupal.org/node/3115223
-  filter/drupal.filter.admin:
-    css:
-      theme:
-        /core/themes/stable/css/filter/filter.admin.css: false
-
-  # @todo remove this library override in https://drupal.org/node/3115223
-  filter/drupal.filter:
-    css:
-      theme:
-        /core/themes/stable/css/filter/filter.admin.css: false
+        css/field_ui.admin.css: css/theme/field-ui.admin.css
 
   views_ui/admin.styling:
     css:
       theme:
-        /core/themes/stable/css/views_ui/views_ui.admin.theme.css: css/theme/views_ui.admin.theme.css
+        css/views_ui.admin.theme.css: css/theme/views_ui.admin.theme.css
 
 libraries-extend:
   ckeditor/drupal.ckeditor:
@@ -125,8 +113,6 @@ libraries-extend:
     - claro/drupal.shortcut
   core/drupal.ajax:
     - claro/ajax
-    # @todo remove claro/temporary.ajax in https://drupal.org/node/3111468
-    - claro/temporary.ajax
   views/views.module:
     - claro/views
   media/media_embed_ckeditor_theme:
diff --git a/core/themes/claro/claro.libraries.yml b/core/themes/claro/claro.libraries.yml
index 6922430426fa..b177fda5b7b7 100644
--- a/core/themes/claro/claro.libraries.yml
+++ b/core/themes/claro/claro.libraries.yml
@@ -323,11 +323,3 @@ classy.search-results:
   css:
     component:
       css/classy/components/search-results.css: {}
-
-# @todo remove library in https://drupal.org/node/3111468
-temporary.ajax:
-  version: VERSION
-  js:
-    js/ajax.temporary.js: {}
-  dependencies:
-    - core/jquery
diff --git a/core/themes/claro/claro.theme b/core/themes/claro/claro.theme
index 8982d6c87030..bfd8348d9c85 100644
--- a/core/themes/claro/claro.theme
+++ b/core/themes/claro/claro.theme
@@ -413,6 +413,19 @@ function claro_preprocess_links(&$variables) {
       }
     }
   }
+
+  // This makes it so array keys of #links items are added as a class. This
+  // functionality was removed in Drupal 8.1, but still neccessary in some
+  // instances.
+  // @todo remove in https://drupal.org/node/3120962
+  if (!empty($variables['links'])) {
+    foreach ($variables['links'] as $key => $value) {
+      if (!is_numeric($key)) {
+        $class = Html::getClass($key);
+        $variables['links'][$key]['attributes']->addClass($class);
+      }
+    }
+  }
 }
 
 /**
@@ -1197,14 +1210,6 @@ function claro_preprocess_image_widget(&$variables) {
     unset($variables['data']['preview']);
   }
 
-  // Stable adds the file size as #suffix for image file_link renderable array.
-  // We have to remove that because we will render it in our file_link template
-  // for every kind of files, and not just for images.
-  if (!empty($variables['element']['fids']['#value'])) {
-    $file = reset($variables['element']['#files']);
-    unset($variables['data']['file_' . $file->id()]['filename']['#suffix']);
-  }
-
   _claro_preprocess_file_and_image_widget($variables);
 }
 
diff --git a/core/themes/claro/css/components/tabledrag.css b/core/themes/claro/css/components/tabledrag.css
index ae680d4ed48e..d63e8dd8f317 100644
--- a/core/themes/claro/css/components/tabledrag.css
+++ b/core/themes/claro/css/components/tabledrag.css
@@ -9,8 +9,7 @@
  * @file
  * Replacement styles for table drag.
  *
- * Replaces both of tabledrag.module.css (from core/stable) and tabledrag.css
- * (from Classy theme).
+ * Replaces core's tabledrag.module.css.
  *
  * @see tabledrag.js
  */
diff --git a/core/themes/claro/css/components/tabledrag.pcss.css b/core/themes/claro/css/components/tabledrag.pcss.css
index ba5171552274..f8c39e41db3d 100644
--- a/core/themes/claro/css/components/tabledrag.pcss.css
+++ b/core/themes/claro/css/components/tabledrag.pcss.css
@@ -2,8 +2,7 @@
  * @file
  * Replacement styles for table drag.
  *
- * Replaces both of tabledrag.module.css (from core/stable) and tabledrag.css
- * (from Classy theme).
+ * Replaces core's tabledrag.module.css.
  *
  * @see tabledrag.js
  */
diff --git a/core/themes/claro/css/theme/views_ui.admin.theme.css b/core/themes/claro/css/theme/views_ui.admin.theme.css
index 0f592906e1e5..b6ff977e292f 100644
--- a/core/themes/claro/css/theme/views_ui.admin.theme.css
+++ b/core/themes/claro/css/theme/views_ui.admin.theme.css
@@ -9,7 +9,7 @@
  * @file
  * View UI admin theme.
  *
- * Replaces the inherited styles provided by Stable.
+ * Replaces the styles provided by the views_ui module.
  */
 
 .views-admin .links {
@@ -37,7 +37,7 @@
 
 .views-admin .icon,
 .views-admin .icon-text {
-  background-image: url(../../images/core/stable/views_ui/sprites.png);
+  background-image: url(../../../../modules/views_ui/images/sprites.png);
   background-repeat: no-repeat;
   background-attachment: scroll;
   background-position: left top; /* LTR */
diff --git a/core/themes/claro/css/theme/views_ui.admin.theme.pcss.css b/core/themes/claro/css/theme/views_ui.admin.theme.pcss.css
index 6b1909cdeada..76e8121cd268 100644
--- a/core/themes/claro/css/theme/views_ui.admin.theme.pcss.css
+++ b/core/themes/claro/css/theme/views_ui.admin.theme.pcss.css
@@ -2,7 +2,7 @@
  * @file
  * View UI admin theme.
  *
- * Replaces the inherited styles provided by Stable.
+ * Replaces the styles provided by the views_ui module.
  */
 
 .views-admin .links {
@@ -25,7 +25,7 @@
 }
 .views-admin .icon,
 .views-admin .icon-text {
-  background-image: url(../../images/core/stable/views_ui/sprites.png);
+  background-image: url(../../../../modules/views_ui/images/sprites.png);
   background-repeat: no-repeat;
   background-attachment: scroll;
   background-position: left top; /* LTR */
diff --git a/core/themes/claro/js/ajax.temporary.es6.js b/core/themes/claro/js/ajax.temporary.es6.js
deleted file mode 100644
index faf0e1c527b3..000000000000
--- a/core/themes/claro/js/ajax.temporary.es6.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * @file
- * Provides theme function for Ajax-related markup.
- *
- * This file is a temporary addition to the theme to override
- * stable/drupal.ajax. This is to help surface potential issues that may arise
- * once the theme no longer depends on stable/drupal.ajax.
- * @todo delete this file in https://drupal.org/node/3111468
- */
-
-(($, Drupal) => {
-  /**
-   * Provide a wrapper for the AJAX progress bar element.
-   *
-   * @param {jQuery} $element
-   *   Progress bar element.
-   * @return {string}
-   *   The HTML markup for the progress bar.
-   */
-  Drupal.theme.ajaxProgressBar = $element =>
-    $('<div class="ajax-progress ajax-progress-bar"></div>').append($element);
-})(jQuery, Drupal);
diff --git a/core/themes/claro/js/ajax.temporary.js b/core/themes/claro/js/ajax.temporary.js
deleted file mode 100644
index 4c1cd8e6ce15..000000000000
--- a/core/themes/claro/js/ajax.temporary.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal) {
-  Drupal.theme.ajaxProgressBar = function ($element) {
-    return $('<div class="ajax-progress ajax-progress-bar"></div>').append($element);
-  };
-})(jQuery, Drupal);
\ No newline at end of file
diff --git a/core/themes/claro/templates/content-edit/image-widget.html.twig b/core/themes/claro/templates/content-edit/image-widget.html.twig
index c0189c2c8cad..43e4558a80b7 100644
--- a/core/themes/claro/templates/content-edit/image-widget.html.twig
+++ b/core/themes/claro/templates/content-edit/image-widget.html.twig
@@ -17,7 +17,6 @@
  *   visible.
  *
  * @see template_preprocess_image_widget()
- * @see stable_preprocess_image_widget()
  * @see claro_preprocess_image_widget()
  */
 #}
diff --git a/core/themes/claro/templates/field/file-link.html.twig b/core/themes/claro/templates/field/file-link.html.twig
index 07cd9e8561da..57b994deba05 100644
--- a/core/themes/claro/templates/field/file-link.html.twig
+++ b/core/themes/claro/templates/field/file-link.html.twig
@@ -11,7 +11,6 @@
  * - file_size: The size of the file.
  *
  * @see template_preprocess_file_link()
- * @see stable_preprocess_image_widget()
  */
 #}
 {{ attach_library('claro/classy.file') }}
diff --git a/core/themes/seven/css/components/system-status-counter.css b/core/themes/seven/css/components/system-status-counter.css
index d26c96890bfe..345c323df71a 100644
--- a/core/themes/seven/css/components/system-status-counter.css
+++ b/core/themes/seven/css/components/system-status-counter.css
@@ -39,15 +39,14 @@
 }
 
 .system-status-counter__status-icon--error:before {
-  background-image: url(../../../stable/images/core/icons/e32700/error.svg);
+  background-image: url(../../../../misc/icons/e32700/error.svg);
 }
 .system-status-counter__status-icon--warning:before {
-  background-image: url(../../../stable/images/core/icons/e29700/warning.svg);
+  background-image: url(../../../../misc/icons/e29700/warning.svg);
 }
 .system-status-counter__status-icon--checked:before {
-  background-image: url(../../../stable/images/core/icons/73b355/check.svg);
+  background-image: url(../../../../misc/icons/73b355/check.svg);
 }
-
 .system-status-counter__status-title {
   display: inline-block;
   padding: 0 6px;
diff --git a/core/themes/seven/css/components/system-status-report.css b/core/themes/seven/css/components/system-status-report.css
index 98527ada154e..3dc7e01ca8e7 100644
--- a/core/themes/seven/css/components/system-status-report.css
+++ b/core/themes/seven/css/components/system-status-report.css
@@ -92,11 +92,11 @@
 }
 .system-status-report__status-icon--error .details-title:before,
 .details .system-status-report__status-icon--error:before {
-  background-image: url(../../../stable/images/core/icons/e32700/error.svg);
+  background-image: url(../../../../misc/icons/e32700/error.svg);
 }
 .system-status-report__status-icon--warning .details-title:before,
 .details .system-status-report__status-icon--warning:before {
-  background-image: url(../../../stable/images/core/icons/e29700/warning.svg);
+  background-image: url(../../../../misc/icons/e29700/warning.svg);
 }
 
 .system-status-report__entry__value {
diff --git a/core/themes/seven/js/ajax.temporary.es6.js b/core/themes/seven/js/ajax.temporary.es6.js
deleted file mode 100644
index faf0e1c527b3..000000000000
--- a/core/themes/seven/js/ajax.temporary.es6.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * @file
- * Provides theme function for Ajax-related markup.
- *
- * This file is a temporary addition to the theme to override
- * stable/drupal.ajax. This is to help surface potential issues that may arise
- * once the theme no longer depends on stable/drupal.ajax.
- * @todo delete this file in https://drupal.org/node/3111468
- */
-
-(($, Drupal) => {
-  /**
-   * Provide a wrapper for the AJAX progress bar element.
-   *
-   * @param {jQuery} $element
-   *   Progress bar element.
-   * @return {string}
-   *   The HTML markup for the progress bar.
-   */
-  Drupal.theme.ajaxProgressBar = $element =>
-    $('<div class="ajax-progress ajax-progress-bar"></div>').append($element);
-})(jQuery, Drupal);
diff --git a/core/themes/seven/js/ajax.temporary.js b/core/themes/seven/js/ajax.temporary.js
deleted file mode 100644
index 4c1cd8e6ce15..000000000000
--- a/core/themes/seven/js/ajax.temporary.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function ($, Drupal) {
-  Drupal.theme.ajaxProgressBar = function ($element) {
-    return $('<div class="ajax-progress ajax-progress-bar"></div>').append($element);
-  };
-})(jQuery, Drupal);
\ No newline at end of file
diff --git a/core/themes/seven/js/user.theme.temporary.es6.js b/core/themes/seven/js/user.theme.temporary.es6.js
deleted file mode 100644
index 461103967de0..000000000000
--- a/core/themes/seven/js/user.theme.temporary.es6.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @file
- * Theme elements for user password forms.
- *
- * This file is a temporary addition to the theme to override
- * stable/drupal.user. This is to help surface potential issues that may arise
- * once the theme no longer depends on stable/drupal.user.
- */
-
-(Drupal => {
-  /**
-   * Constructs a password confirm message element
-   *
-   * @return {string}
-   *   A string representing a DOM fragment.
-   */
-  Drupal.theme.passwordConfirmMessage = translate =>
-    `<div aria-live="polite" aria-atomic="true" class="password-confirm-message js-password-confirm-message">${translate.confirmTitle} <span></span></div>`;
-})(Drupal);
diff --git a/core/themes/seven/js/user.theme.temporary.js b/core/themes/seven/js/user.theme.temporary.js
deleted file mode 100644
index 4e00e623192a..000000000000
--- a/core/themes/seven/js/user.theme.temporary.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
-* DO NOT EDIT THIS FILE.
-* See the following change record for more information,
-* https://www.drupal.org/node/2815083
-* @preserve
-**/
-
-(function (Drupal) {
-  Drupal.theme.passwordConfirmMessage = function (translate) {
-    return "<div aria-live=\"polite\" aria-atomic=\"true\" class=\"password-confirm-message js-password-confirm-message\">".concat(translate.confirmTitle, " <span></span></div>");
-  };
-})(Drupal);
\ No newline at end of file
diff --git a/core/themes/seven/seven.info.yml b/core/themes/seven/seven.info.yml
index 96b88a21c6f7..44f08011a4be 100644
--- a/core/themes/seven/seven.info.yml
+++ b/core/themes/seven/seven.info.yml
@@ -13,7 +13,7 @@
 # changes.
 name: Seven
 type: theme
-base theme: stable
+base theme: false
 description: 'The default administration theme for Drupal 8 was designed with clean lines, simple blocks, and sans-serif font to emphasize the tools and tasks at hand.'
 alt text: 'Default administration theme for Drupal 8 with simple blocks and clean lines.'
 package: Core
@@ -26,14 +26,14 @@ libraries-override:
   system/base:
     css:
       component:
-        /core/themes/stable/css/system/components/system-status-counter.css: css/components/system-status-counter.css
-        /core/themes/stable/css/system/components/system-status-report-counters.css: css/components/system-status-report-counters.css
-        /core/themes/stable/css/system/components/system-status-report-general-info.css: css/components/system-status-report-general-info.css
+        css/components/system-status-counter.css: css/components/system-status-counter.css
+        css/components/system-status-report-counters.css: css/components/system-status-report-counters.css
+        css/components/system-status-report-general-info.css: css/components/system-status-report-general-info.css
 
   core/drupal.vertical-tabs:
     css:
       component:
-        /core/themes/stable/css/core/vertical-tabs.css: false
+        misc/vertical-tabs.css: false
   core/jquery.ui:
     css:
       theme:
@@ -43,28 +43,9 @@ libraries-override:
       component:
         assets/vendor/jquery.ui/themes/base/dialog.css: false
 
-  # @todo remove this library override in https://drupal.org/node/3115223
-  filter/drupal.filter.admin:
-    css:
-      theme:
-        /core/themes/stable/css/filter/filter.admin.css: false
-
-  # @todo remove this library override in https://drupal.org/node/3115223
-  filter/drupal.filter:
-    css:
-      theme:
-        /core/themes/stable/css/filter/filter.admin.css: false
-  # @todo remove this library override in https://drupal.org/node/3111468
-  stable/drupal.ajax: seven/empty
-  # @todo remove this library override in https://drupal.org/node/3111468
-  stable/drupal.user: seven/empty
-
 libraries-extend:
   core/ckeditor:
     - seven/ckeditor-dialog
-  # @todo remove this library extend in https://drupal.org/node/3111468
-  core/drupal.ajax:
-    - seven/temporary.ajax
   core/drupal.dialog:
     - seven/seven.drupal.dialog
   core/drupal.dropbutton:
diff --git a/core/themes/seven/seven.libraries.yml b/core/themes/seven/seven.libraries.yml
index 689deb7c0d0d..060c0002960e 100644
--- a/core/themes/seven/seven.libraries.yml
+++ b/core/themes/seven/seven.libraries.yml
@@ -1,12 +1,3 @@
-# Drupal throws an IncompleteLibraryDefinitionException if a base theme defined
-# library is set to false in a subtheme's libraries-override. This empty library
-# is used as a workaround.
-# @see https://www.drupal.org/node/3098375
-#  @todo remove this in https://drupal.org/node/3111468
-empty:
-  version: VERSION
-  css: {}
-
 global-styling:
   version: VERSION
   css:
@@ -183,9 +174,6 @@ user:
   css:
     component:
       css/components/user.css: { weight: -10 }
-  js:
-    # @todo remove this file from library in https://drupal.org/node/3111468
-    js/user.theme.temporary.js: {}
 
 filter:
   version: VERSION
@@ -263,11 +251,3 @@ classy.search-results:
   css:
     component:
       css/classy/components/search-results.css: {}
-
-# @todo remove library in https://drupal.org/node/3111468
-temporary.ajax:
-  version: VERSION
-  js:
-    js/ajax.temporary.js: {}
-  dependencies:
-    - core/jquery
diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme
index 8caf69ae3c79..58cd04ddba1a 100644
--- a/core/themes/seven/seven.theme
+++ b/core/themes/seven/seven.theme
@@ -5,6 +5,7 @@
  * Functions to support theming in the Seven theme.
  */
 
+use Drupal\Component\Utility\Html;
 use Drupal\Core\Url;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\media\MediaForm;
@@ -445,3 +446,23 @@ function seven_preprocess_image_widget(array &$variables) {
 function seven_preprocess_update_version(array &$variables) {
   $variables['#attached']['library'][] = 'seven/update-report';
 }
+
+/**
+ * Implements template_preprocess_links().
+ *
+ * This makes it so array keys of #links items are added as a class. This
+ * functionality was removed in Drupal 8.1, but still neccessary in some
+ * instances.
+ *
+ * @todo remove in https://drupal.org/node/3120962
+ */
+function seven_preprocess_links(&$variables) {
+  if (!empty($variables['links'])) {
+    foreach ($variables['links'] as $key => $value) {
+      if (!is_numeric($key)) {
+        $class = Html::getClass($key);
+        $variables['links'][$key]['attributes']->addClass($class);
+      }
+    }
+  }
+}
diff --git a/core/themes/seven/templates/classy/field/file-link.html.twig b/core/themes/seven/templates/field/file-link.html.twig
similarity index 90%
rename from core/themes/seven/templates/classy/field/file-link.html.twig
rename to core/themes/seven/templates/field/file-link.html.twig
index fbf11fbc572f..739fc0f7e6ad 100644
--- a/core/themes/seven/templates/classy/field/file-link.html.twig
+++ b/core/themes/seven/templates/field/file-link.html.twig
@@ -10,7 +10,6 @@
  * - file_size: The size of the file.
  *
  * @see template_preprocess_file_link()
- * @see stable_preprocess_image_widget()
  */
 #}
 {{ attach_library('seven/classy.file') }}
-- 
GitLab