diff --git a/core/.phpstan-baseline.php b/core/.phpstan-baseline.php
index 0abeb6f41cba47ac08bad74185d5982d1320c413..dff06b76d3c41df57e770d3e0fc78e9208603d7a 100644
--- a/core/.phpstan-baseline.php
+++ b/core/.phpstan-baseline.php
@@ -52983,18 +52983,7 @@
 	'path' => __DIR__ . '/tests/Drupal/Tests/BrowserTestBase.php',
 ];
 $ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\Tests\\\\BrowserTestBase\\:\\:htmlOutput\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/tests/Drupal/Tests/BrowserTestBase.php',
-];
-$ignoreErrors[] = [
-	'message' => '#^Method Drupal\\\\Tests\\\\BrowserTestBase\\:\\:initBrowserOutputFile\\(\\) has no return type specified\\.$#',
-	'identifier' => 'missingType.return',
-	'count' => 1,
-	'path' => __DIR__ . '/tests/Drupal/Tests/BrowserTestBase.php',
-];
-$ignoreErrors[] = [
+	// identifier: missingType.return
 	'message' => '#^Method Drupal\\\\Tests\\\\BrowserTestBase\\:\\:initConfig\\(\\) has no return type specified\\.$#',
 	'identifier' => 'missingType.return',
 	'count' => 1,
diff --git a/core/modules/navigation/tests/src/Functional/NavigationContentTopTest.php b/core/modules/navigation/tests/src/Kernel/NavigationContentTopTest.php
similarity index 71%
rename from core/modules/navigation/tests/src/Functional/NavigationContentTopTest.php
rename to core/modules/navigation/tests/src/Kernel/NavigationContentTopTest.php
index 981b628259c7d15f8031d8a1536e4242867cb845..7782612877029eb6b3e42a7346d76645851d33a9 100644
--- a/core/modules/navigation/tests/src/Functional/NavigationContentTopTest.php
+++ b/core/modules/navigation/tests/src/Kernel/NavigationContentTopTest.php
@@ -2,11 +2,13 @@
 
 declare(strict_types=1);
 
-namespace Drupal\Tests\navigation\Functional;
+namespace Drupal\Tests\navigation\Kernel;
 
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Url;
-use Drupal\Tests\BrowserTestBase;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\Tests\HttpKernelUiHelperTrait;
+use Drupal\Tests\user\Traits\UserCreationTrait;
 
 // cspell:ignore foobarbaz baznew
 
@@ -15,17 +17,15 @@
  *
  * @group navigation
  */
-class NavigationContentTopTest extends BrowserTestBase {
+class NavigationContentTopTest extends KernelTestBase {
 
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = ['navigation', 'navigation_test', 'test_page_test'];
+  use HttpKernelUiHelperTrait;
+  use UserCreationTrait;
 
   /**
    * {@inheritdoc}
    */
-  protected $defaultTheme = 'stark';
+  protected static $modules = ['navigation', 'navigation_test', 'test_page_test', 'user', 'system', 'layout_builder', 'layout_discovery'];
 
   /**
    * {@inheritdoc}
@@ -33,7 +33,9 @@ class NavigationContentTopTest extends BrowserTestBase {
   protected function setUp(): void {
     parent::setUp();
 
-    $this->drupalLogin($this->createUser([
+    $this->installConfig('navigation');
+    $this->installEntitySchema('user');
+    $this->setCurrentUser($this->createUser([
       'access navigation',
     ]));
   }
diff --git a/core/modules/navigation/tests/src/Functional/NavigationDefaultBlockDefinitionTest.php b/core/modules/navigation/tests/src/Kernel/NavigationDefaultBlockDefinitionTest.php
similarity index 74%
rename from core/modules/navigation/tests/src/Functional/NavigationDefaultBlockDefinitionTest.php
rename to core/modules/navigation/tests/src/Kernel/NavigationDefaultBlockDefinitionTest.php
index 45cd4a2b20db7b39abb4e8eb14823a3e2fed3ef5..e39cb532fa5955f2b544cfd26fd2837bd796e50b 100644
--- a/core/modules/navigation/tests/src/Functional/NavigationDefaultBlockDefinitionTest.php
+++ b/core/modules/navigation/tests/src/Kernel/NavigationDefaultBlockDefinitionTest.php
@@ -2,27 +2,35 @@
 
 declare(strict_types=1);
 
-namespace Drupal\Tests\navigation\Functional;
+namespace Drupal\Tests\navigation\Kernel;
 
 use Drupal\Core\Url;
-use Drupal\Tests\BrowserTestBase;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\Tests\HttpKernelUiHelperTrait;
+use Drupal\Tests\user\Traits\UserCreationTrait;
 
 /**
  * Tests the default block provider logic.
  *
  * @group navigation
  */
-class NavigationDefaultBlockDefinitionTest extends BrowserTestBase {
+class NavigationDefaultBlockDefinitionTest extends KernelTestBase {
+
+  use HttpKernelUiHelperTrait;
+  use UserCreationTrait;
 
   /**
    * {@inheritdoc}
    */
-  protected static $modules = ['test_page_test', 'block'];
+  protected static $modules = ['test_page_test', 'block', 'user', 'system', 'layout_discovery', 'layout_builder'];
 
   /**
    * {@inheritdoc}
    */
-  protected $defaultTheme = 'stark';
+  protected function setUp(): void {
+    parent::setUp();
+    $this->installEntitySchema('user');
+  }
 
   /**
    * Tests the default block flow enabling Navigation module first.
@@ -33,7 +41,8 @@ public function testNavigationDefaultAfterNavigation(): void {
 
     // After installing Navigation, the bar is present, but not the block.
     $module_installer->install(['navigation']);
-    $this->drupalLogin($this->drupalCreateUser(['access navigation']));
+    $this->installConfig(['navigation']);
+    $this->setCurrentUser($this->createUser(['access navigation']));
     $this->drupalGet($test_page_url);
     $this->assertSession()->elementExists('css', '.admin-toolbar');
     $this->assertSession()->elementNotExists('css', '.toolbar-button--icon--test-block');
@@ -60,7 +69,8 @@ public function testNavigationDefaultBeforeNavigation(): void {
 
     // After installing Navigation, both elements are present.
     $module_installer->install(['navigation']);
-    $this->drupalLogin($this->drupalCreateUser(['access navigation']));
+    $this->installConfig(['navigation']);
+    $this->setCurrentUser($this->createUser(['access navigation']));
     $this->drupalGet($test_page_url);
     $this->assertSession()->elementExists('css', '.admin-toolbar');
     $this->assertSession()->elementContains('css', '.toolbar-button--icon--test-block', 'Test Navigation Block');
diff --git a/core/modules/node/tests/src/Functional/NodeLinksTest.php b/core/modules/node/tests/src/Functional/NodeLinksTest.php
deleted file mode 100644
index cb70b80a9cf855db7465c9f2301f2a86ed25aeac..0000000000000000000000000000000000000000
--- a/core/modules/node/tests/src/Functional/NodeLinksTest.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Drupal\Tests\node\Functional;
-
-use Drupal\node\NodeInterface;
-
-/**
- * Tests the output of node links (read more, add new comment, etc).
- *
- * @group node
- */
-class NodeLinksTest extends NodeTestBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected static $modules = ['views'];
-
-  /**
-   * {@inheritdoc}
-   */
-  protected $defaultTheme = 'stark';
-
-  /**
-   * Tests that the links can be hidden in the view display settings.
-   */
-  public function testHideLinks(): void {
-    $node = $this->drupalCreateNode([
-      'type' => 'article',
-      'promote' => NodeInterface::PROMOTED,
-    ]);
-
-    // Links are displayed by default.
-    $this->drupalGet('node');
-    $this->assertSession()->pageTextContains($node->getTitle());
-    $this->assertSession()->linkExists('Read more');
-
-    // Hide links.
-    \Drupal::service('entity_display.repository')
-      ->getViewDisplay('node', 'article', 'teaser')
-      ->removeComponent('links')
-      ->save();
-
-    $this->drupalGet('node');
-    $this->assertSession()->pageTextContains($node->getTitle());
-    $this->assertSession()->linkNotExists('Read more');
-  }
-
-}
diff --git a/core/modules/node/tests/src/Kernel/NodeLinksTest.php b/core/modules/node/tests/src/Kernel/NodeLinksTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..eaf1de622c1d00d47a3deae233311c05245b3b9f
--- /dev/null
+++ b/core/modules/node/tests/src/Kernel/NodeLinksTest.php
@@ -0,0 +1,93 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\node\Kernel;
+
+use Drupal\Core\Datetime\Entity\DateFormat;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\node\Entity\NodeType;
+use Drupal\node\NodeInterface;
+use Drupal\Tests\HttpKernelUiHelperTrait;
+use Drupal\Tests\node\Traits\NodeCreationTrait;
+use Drupal\Tests\user\Traits\UserCreationTrait;
+
+/**
+ * Tests the output of node links (read more, add new comment, etc).
+ *
+ * @group node
+ */
+class NodeLinksTest extends KernelTestBase {
+
+  use HttpKernelUiHelperTrait;
+  use UserCreationTrait;
+  use NodeCreationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'system',
+    'user',
+    'field',
+    'datetime',
+    'filter',
+    'text',
+    'node',
+    'views',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    $this->installEntitySchema('user');
+    $this->installEntitySchema('node');
+    $this->installConfig(['filter']);
+    $this->installConfig(['node']);
+
+    $this->setUpCurrentUser(permissions: [
+      'access content',
+    ]);
+
+    DateFormat::create([
+      'id' => 'fallback',
+      'label' => 'Fallback',
+      'pattern' => 'Y-m-d',
+    ])->save();
+
+    $node_type = NodeType::create([
+      'type' => 'article',
+      'name' => 'Article',
+    ]);
+    $node_type->save();
+  }
+
+  /**
+   * Tests that the links can be hidden in the view display settings.
+   */
+  public function testHideLinks(): void {
+    $node = $this->createNode([
+      'type' => 'article',
+      'promote' => NodeInterface::PROMOTED,
+    ]);
+
+    // Links are displayed by default.
+    $this->drupalGet('node');
+    $this->assertSession()->pageTextContains($node->getTitle());
+    $this->assertSession()->linkExists('Read more');
+
+    // Hide links.
+    \Drupal::service('entity_display.repository')
+      ->getViewDisplay('node', 'article', 'teaser')
+      ->removeComponent('links')
+      ->save();
+
+    $this->drupalGet('node');
+    $this->assertSession()->pageTextContains($node->getTitle());
+    $this->assertSession()->linkNotExists('Read more');
+  }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php b/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php
index e782646e1cd70d202ef85fbb230f9919afad37d3..a3b20727863442cbf07ed667bac7ccc949cb940b 100644
--- a/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/TermIndexTest.php
@@ -4,7 +4,6 @@
 
 namespace Drupal\Tests\taxonomy\Functional;
 
-use Drupal\Core\Link;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 
@@ -235,22 +234,4 @@ public function testTaxonomyIndex(): void {
     $this->assertEquals(0, $index_count, 'Term 2 is not indexed.');
   }
 
-  /**
-   * Tests that there is a link to the parent term on the child term page.
-   */
-  public function testTaxonomyTermHierarchyBreadcrumbs(): void {
-    // Create two taxonomy terms and set term2 as the parent of term1.
-    $term1 = $this->createTerm($this->vocabulary);
-    $term2 = $this->createTerm($this->vocabulary);
-    $term1->parent = [$term2->id()];
-    $term1->save();
-
-    // Verify that the page breadcrumbs include a link to the parent term.
-    $this->drupalGet('taxonomy/term/' . $term1->id());
-    // Breadcrumbs are not rendered with a language, prevent the term
-    // language from being added to the options.
-    // Check that parent term link is displayed when viewing the node.
-    $this->assertSession()->responseContains(Link::fromTextAndUrl($term2->getName(), $term2->toUrl('canonical', ['language' => NULL]))->toString());
-  }
-
 }
diff --git a/core/modules/taxonomy/tests/src/Kernel/TermHierarchyTest.php b/core/modules/taxonomy/tests/src/Kernel/TermHierarchyTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..829989e64aa648dbbcc264b4fa3811eabcf56236
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Kernel/TermHierarchyTest.php
@@ -0,0 +1,84 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\taxonomy\Kernel;
+
+use Drupal\Core\Link;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\Tests\block\Traits\BlockCreationTrait;
+use Drupal\Tests\HttpKernelUiHelperTrait;
+use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
+use Drupal\Tests\user\Traits\UserCreationTrait;
+
+/**
+ * Tests term hierarchy.
+ *
+ * @group taxonomy
+ */
+class TermHierarchyTest extends KernelTestBase {
+
+  use HttpKernelUiHelperTrait;
+  use UserCreationTrait;
+  use BlockCreationTrait;
+  use TaxonomyTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'system',
+    'user',
+    'block',
+    'node',
+    'filter',
+    'text',
+    'taxonomy',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+    $this->installConfig(['filter']);
+    $this->installEntitySchema('taxonomy_term');
+    $this->installEntitySchema('user');
+
+    $this->container->get('theme_installer')->install(['stark']);
+    $this->container->get('theme.manager')->setActiveTheme(
+      $this->container->get('theme.initialization')->initTheme('stark')
+    );
+
+    $this->placeBlock('system_breadcrumb_block', [
+      'region' => 'content',
+      'theme' => 'stark',
+    ]);
+
+    $this->setUpCurrentUser(permissions: [
+      'administer taxonomy',
+      'bypass node access',
+    ]);
+  }
+
+  /**
+   * Tests that there is a link to the parent term on the child term page.
+   */
+  public function testTaxonomyTermHierarchyBreadcrumbs(): void {
+    $vocabulary = $this->createVocabulary();
+
+    // Create two taxonomy terms and set term2 as the parent of term1.
+    $term1 = $this->createTerm($vocabulary);
+    $term2 = $this->createTerm($vocabulary);
+    $term1->parent = [$term2->id()];
+    $term1->save();
+
+    // Verify that the page breadcrumbs include a link to the parent term.
+    $this->drupalGet('taxonomy/term/' . $term1->id());
+    // Breadcrumbs are not rendered with a language, prevent the term
+    // language from being added to the options.
+    // Check that parent term link is displayed when viewing the node.
+    $this->assertSession()->responseContains(Link::fromTextAndUrl($term2->getName(), $term2->toUrl('canonical', ['language' => NULL]))->toString());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/BrowserHtmlDebugTrait.php b/core/tests/Drupal/Tests/BrowserHtmlDebugTrait.php
index 95c568afd683fb7ed1489d95162de133ca260386..d71c38caa828cd32b0526c1c527dfea711a54503 100644
--- a/core/tests/Drupal/Tests/BrowserHtmlDebugTrait.php
+++ b/core/tests/Drupal/Tests/BrowserHtmlDebugTrait.php
@@ -105,6 +105,8 @@ protected function getHtmlOutputHeaders() {
    *   current page content is used.
    *
    * @see \Drupal\Tests\Listeners\VerbosePrinter::printResult()
+   *
+   * @phpstan-ignore missingType.return
    */
   protected function htmlOutput($message = NULL) {
     if (!$this->htmlOutputEnabled) {
@@ -123,6 +125,8 @@ protected function htmlOutput($message = NULL) {
 
   /**
    * Creates the directory to store browser output.
+   *
+   * @phpstan-ignore missingType.return
    */
   protected function initBrowserOutputFile() {
     $browserOutputFile = getenv('BROWSERTEST_OUTPUT_FILE');
@@ -143,7 +147,7 @@ protected function initBrowserOutputFile() {
         file_put_contents($this->htmlOutputDirectory . '/.htaccess', "<IfModule mod_expires.c>\nExpiresActive Off\n</IfModule>\n");
       }
       $this->htmlOutputCounterStorage = $this->htmlOutputDirectory . '/' . $this->htmlOutputClassName . '.counter';
-      $this->htmlOutputTestId = str_replace('sites/simpletest/', '', $this->siteDirectory);
+      $this->htmlOutputTestId = str_replace(['sites/simpletest/', 'vfs://root/'], '', $this->siteDirectory);
       if (is_file($this->htmlOutputCounterStorage)) {
         $this->htmlOutputCounter = max(1, (int) file_get_contents($this->htmlOutputCounterStorage)) + 1;
       }
diff --git a/core/tests/Drupal/Tests/HttpKernelUiHelperTrait.php b/core/tests/Drupal/Tests/HttpKernelUiHelperTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b71ce94be3f358b3c5cbc484bf6fdd353a3d69e
--- /dev/null
+++ b/core/tests/Drupal/Tests/HttpKernelUiHelperTrait.php
@@ -0,0 +1,167 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests;
+
+use Behat\Mink\Driver\BrowserKitDriver;
+use Behat\Mink\Driver\DriverInterface;
+use Behat\Mink\Mink;
+use Behat\Mink\Selector\SelectorsHandler;
+use Behat\Mink\Session;
+use Drupal\Core\Test\RefreshVariablesTrait;
+use Drupal\Core\Url;
+use Symfony\Component\HttpKernel\HttpKernelBrowser;
+
+/**
+ * Provides UI helper methods using the HTTP kernel to make requests.
+ *
+ * This can be used by Kernel tests.
+ */
+trait HttpKernelUiHelperTrait {
+
+  use BrowserHtmlDebugTrait;
+  use RefreshVariablesTrait;
+
+  /**
+   * Mink session manager.
+   *
+   * This is lazily initialized by the first call to self::drupalGet().
+   *
+   * @var \Behat\Mink\Mink|null
+   */
+  protected ?Mink $mink;
+
+  /**
+   * Retrieves a Drupal path.
+   *
+   * Requests are sent to the HTTP kernel.
+   *
+   * There is no logged in user. Use \Drupal\Tests\user\Traits\UserCreationTrait
+   * to set a current user.
+   *
+   * There is no active theme. To place blocks, a test must first install a
+   * theme and then set it as active.
+   *
+   * Page caching modules will not function, because the
+   * CommandLineOrUnsafeMethod caching policy will deny caching.
+   *
+   * Some requests may not function correctly because DrupalKernel is not
+   * explicitly designed to handle multiple requests: see
+   * https://www.drupal.org/project/drupal/issues/2708827.
+   *
+   * @param string|\Drupal\Core\Url $path
+   *   Drupal path or URL to load into Mink controlled browser.
+   * @param array $options
+   *   (optional) Options to be forwarded to the URL generator.
+   * @param string[] $headers
+   *   An array containing additional HTTP request headers, the array keys are
+   *   the header names and the array values the header values. This is useful
+   *   to set for example the "Accept-Language" header for requesting the page
+   *   in a different language. Note that not all headers are supported, for
+   *   example the "Accept" header is always overridden by the browser. For
+   *   testing REST APIs it is recommended to obtain a separate HTTP client
+   *   using getHttpClient() and performing requests that way.
+   *
+   * @return string
+   *   The retrieved HTML string.
+   *
+   * @see \Drupal\Tests\BrowserTestBase::getHttpClient()
+   */
+  protected function drupalGet($path, array $options = [], array $headers = []): string {
+    // Convert a Url object to a string.
+    if ($path instanceof Url) {
+      $url_options = $path->getOptions();
+      $options = $url_options + $options;
+      $path->setOptions($options);
+      $path = $path->setAbsolute(FALSE)->toString();
+    }
+
+    $session = $this->getSession();
+
+    $session->visit($path);
+
+    $out = $session->getPage()->getContent();
+    $this->content = $out;
+
+    // Ensure that any changes to variables in the other thread are picked up.
+    $this->refreshVariables();
+
+    if ($this->htmlOutputEnabled) {
+      $html_output = 'GET request to: ' . $path;
+      $html_output .= '<hr />' . $out;
+      $html_output .= $this->getHtmlOutputHeaders();
+      $this->htmlOutput($html_output);
+    }
+
+    return $out;
+  }
+
+  /**
+   * Returns Mink session.
+   *
+   * @param string $name
+   *   (optional) Name of the session. Defaults to the active session.
+   *
+   * @return \Behat\Mink\Session
+   *   The active Mink session object.
+   */
+  public function getSession($name = NULL): Session {
+    // Lazily initialize the Mink session. We do this because unlike Browser
+    // tests where there should definitely be requests made, this is not
+    // necessarily the case with Kernel tests.
+    if (!isset($this->mink)) {
+      $this->initMink();
+      // Set up the browser test output file.
+      $this->initBrowserOutputFile();
+    }
+
+    return $this->mink->getSession($name);
+  }
+
+  /**
+   * Initializes Mink sessions.
+   */
+  protected function initMink(): void {
+    $driver = $this->getDefaultDriverInstance();
+
+    $selectors_handler = new SelectorsHandler([
+      'hidden_field_selector' => new HiddenFieldSelector(),
+    ]);
+    $session = new Session($driver, $selectors_handler);
+    $this->mink = new Mink();
+    $this->mink->registerSession('default', $session);
+    $this->mink->setDefaultSessionName('default');
+  }
+
+  /**
+   * Gets an instance of the default Mink driver.
+   *
+   * @return \Behat\Mink\Driver\DriverInterface
+   *   Instance of default Mink driver.
+   *
+   * @throws \InvalidArgumentException
+   *   When provided default Mink driver class can't be instantiated.
+   */
+  protected function getDefaultDriverInstance(): DriverInterface {
+    $http_kernel = $this->container->get('http_kernel');
+    $browserkit_client = new HttpKernelBrowser($http_kernel);
+    $driver = new BrowserKitDriver($browserkit_client);
+    return $driver;
+  }
+
+  /**
+   * Returns WebAssert object.
+   *
+   * @param string $name
+   *   (optional) Name of the session. Defaults to the active session.
+   *
+   * @return \Drupal\Tests\WebAssert
+   *   A new web-assert option for asserting the presence of elements with.
+   */
+  public function assertSession($name = NULL): WebAssert {
+    $this->addToAssertionCount(1);
+    return new WebAssert($this->getSession($name));
+  }
+
+}