diff --git a/src/Controller/BrowserController.php b/src/Controller/BrowserController.php
index b785f5e7f392f49ea2460647da67fe0fce2859f1..3c0e05030287cfb0b19da6f7960edb74d54809e2 100644
--- a/src/Controller/BrowserController.php
+++ b/src/Controller/BrowserController.php
@@ -132,9 +132,10 @@ class BrowserController extends ControllerBase {
     // To get common data from single source plugin.
     $current_source = reset($current_sources);
 
-    $sort_options = [];
+    $sort_options = $active_plugins = [];
     foreach ($current_sources as $source) {
       $sort_options[$source->getPluginId()] = array_values($source->getSortOptions());
+      $active_plugins[$source->getPluginId()] = $source->getPluginDefinition()['label'];
     }
 
     return [
@@ -145,6 +146,7 @@ class BrowserController extends ControllerBase {
         ],
         'drupalSettings' => [
           'project_browser' => [
+            'active_plugins' => $active_plugins,
             'modules' => $modules_status,
             'drupal_version' => \Drupal::VERSION,
             'drupal_core_compatibility' => \Drupal::CORE_COMPATIBILITY,
diff --git a/sveltejs/public/build/bundle.css b/sveltejs/public/build/bundle.css
index fafdf31ddab767ea9f5726ed35690f1b868b9d3d..b3af48a31efa4e366e4f8acf253ce04396f92266 100644
Binary files a/sveltejs/public/build/bundle.css and b/sveltejs/public/build/bundle.css differ
diff --git a/sveltejs/public/build/bundle.js b/sveltejs/public/build/bundle.js
index 98495ee24d800de4ca78790a57dab725bb8585ef..b50a1275160b7c155751ee1f2eeb1d2897d44d57 100644
Binary files a/sveltejs/public/build/bundle.js and b/sveltejs/public/build/bundle.js differ
diff --git a/sveltejs/public/build/bundle.js.map b/sveltejs/public/build/bundle.js.map
index a1b470754c331e7e0a63a348cc177ef1cce57ef1..2e4d71aaada25084f66b225475bc10770b1c1a1a 100644
Binary files a/sveltejs/public/build/bundle.js.map and b/sveltejs/public/build/bundle.js.map differ
diff --git a/sveltejs/src/ProjectBrowser.svelte b/sveltejs/src/ProjectBrowser.svelte
index 5e78168d8c5b28cc1d9a3184f57a301909774b77..a68dfa4eb6d8ff477246a01edec6494ff0206844 100644
--- a/sveltejs/src/ProjectBrowser.svelte
+++ b/sveltejs/src/ProjectBrowser.svelte
@@ -33,10 +33,12 @@
     MODULE_STATUS,
     ALLOW_UI_INSTALL,
     PM_VALIDATION_ERROR,
+    ACTIVE_PLUGINS,
   } from './constants';
   // cspell:ignore tabwise
 
   const { Drupal } = window;
+  const { announce } = Drupal;
 
   let data;
   let rows = [];
@@ -45,6 +47,7 @@
   const pageIndex = 0; // first row
 
   let loading = true;
+  let sortText = $sortCriteria.find((option) => option.id === $sort).text;
   // eslint-disable-next-line import/no-mutable-exports,import/prefer-default-export
   export let searchText;
   searchString.subscribe((value) => {
@@ -61,6 +64,7 @@
     element = value;
   });
   let filterComponent;
+  let searchComponent;
 
   /**
    * Load data from Drupal.org API.
@@ -184,6 +188,7 @@
   }
   async function onSort(event) {
     sort.set(event.detail.sort);
+    sortText = $sortCriteria.find((option) => option.id === $sort).text;
     await load(0);
     page.set(0);
   }
@@ -223,6 +228,34 @@
     await load(0);
   }
 
+  /**
+   * Refreshes the live region after a filter or search completes.
+   */
+  const refreshLiveRegion = () => {
+    if ($rowsCount) {
+      // Set announce() to an empty string. This ensures the result count will
+      // be announced after filtering even if the count is the same.
+      announce('');
+
+      // The announcement is delayed by 210 milliseconds, a wait that is
+      // slightly longer than the 200 millisecond debounce() built into
+      // announce(). This ensures that the above call to reset the aria live
+      // region to an empty string actually takes place instead of being
+      // debounced.
+      setTimeout(() => {
+        announce(
+          Drupal.t('@count Results for @active_tab, Sorted by @sortText', {
+            '@count': $rowsCount
+              .toString()
+              .replace(/\B(?=(\d{3})+(?!\d))/g, ','),
+            '@sortText': sortText,
+            '@active_tab': ACTIVE_PLUGINS[$activeTab],
+          }),
+        );
+      }, 210);
+    }
+  };
+
   document.onmouseover = function setInnerDocClickTrue() {
     window.innerDocClick = true;
   };
@@ -253,48 +286,66 @@
   <ProjectGrid {loading} {rows} {pageIndex} {$pageSize} let:rows>
     <div slot="top">
       <Search
+        bind:this={searchComponent}
         on:search={onSearch}
         on:sort={onSort}
         on:advancedFilter={onAdvancedFilter}
         on:selectCategory={onSelectCategory}
         {searchText}
+        {refreshLiveRegion}
       />
-      {#if matches}
-        <div class="project-browser__toggle-buttons">
-          <button
-            class:project-browser__selected-tab={toggleView === 'List'}
-            class="project-browser__toggle project-browser__list-button"
-            value="List"
-            on:click={(e) => {
-              toggleView = 'List';
-              onToggle(e.target.value);
-            }}
-          >
-            <img
-              class="project-browser__list-icon"
-              src="{FULL_MODULE_PATH}/images/list.svg"
-              alt=""
-            />
-            {Drupal.t('List')}
-          </button>
-          <button
-            class:project-browser__selected-tab={toggleView === 'Grid'}
-            class="project-browser__toggle project-browser__grid-button"
-            value="Grid"
-            on:click={(e) => {
-              toggleView = 'Grid';
-              onToggle(e.target.value);
-            }}
-          >
-            <img
-              class="project-browser__grid-icon"
-              src="{FULL_MODULE_PATH}/images/grid-fill.svg"
-              alt=""
-            />
-            {Drupal.t('Grid')}
-          </button>
+
+      <div class="search-results-wrapper">
+        <div class="search-results">
+          {#each dataArray as dataValue}
+            {#if $activeTab === dataValue.pluginId}
+              <span id="output">
+                {$rowsCount &&
+                  $rowsCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
+                {Drupal.t('Results')}
+              </span>
+            {/if}
+          {/each}
         </div>
-      {/if}
+
+        {#if matches}
+          <div class="project-browser__toggle-buttons">
+            <button
+              class:project-browser__selected-tab={toggleView === 'List'}
+              class="project-browser__toggle project-browser__list-button"
+              value="List"
+              on:click={(e) => {
+                toggleView = 'List';
+                onToggle(e.target.value);
+              }}
+            >
+              <img
+                class="project-browser__list-icon"
+                src="{FULL_MODULE_PATH}/images/list.svg"
+                alt=""
+              />
+              {Drupal.t('List')}
+            </button>
+            <button
+              class:project-browser__selected-tab={toggleView === 'Grid'}
+              class="project-browser__toggle project-browser__grid-button"
+              value="Grid"
+              on:click={(e) => {
+                toggleView = 'Grid';
+                onToggle(e.target.value);
+              }}
+            >
+              <img
+                class="project-browser__grid-icon"
+                src="{FULL_MODULE_PATH}/images/grid-fill.svg"
+                alt=""
+              />
+              {Drupal.t('Grid')}
+            </button>
+          </div>
+        {/if}
+      </div>
+
       {#if dataArray.length >= 2}
         <nav aria-label={Drupal.t('Plugin tabs')}>
           <div class="project-browser__plugin-tabs">
@@ -306,11 +357,10 @@
                 value={dataValue.pluginId}
                 on:click={(e) => {
                   toggleRows(e.target.value);
+                  searchComponent.onSearch(e);
                 }}
               >
                 {dataValue.pluginLabel}
-                {dataValue.totalResults}
-                {Drupal.t('Results')}
               </button>
             {/each}
           </div>
@@ -382,6 +432,7 @@
   .project-browser__toggle-buttons {
     display: flex;
     margin-inline-end: 25px;
+    font-weight: bold;
   }
   .project-browser__toggle:focus {
     box-shadow: 0 0 0 2px #fff, 0 0 0 5px #26a769;
@@ -413,6 +464,11 @@
   .project-browser__plugin-tabs .project-browser__toggle {
     margin-inline-start: 0;
   }
+  .search-results {
+    font-weight: bold;
+    margin-inline-start: 10px;
+    margin-bottom: 5px;
+  }
   .project-browser__install-warning {
     border: 1px solid red;
     padding: 1em;
@@ -423,6 +479,20 @@
   .project-browser__warning-header {
     color: red;
   }
+  .search-results-wrapper {
+    display: flex;
+    justify-content: space-between;
+  }
+  #output {
+    display: inline-block;
+    font-family: sans-serif;
+    font-style: normal;
+    font-weight: 700;
+    font-size: 14px;
+    line-height: 21px;
+    margin-left: 20px;
+  }
+
   @media (forced-colors: active) {
     .project-browser__toggle {
       border: 1px solid;
diff --git a/sveltejs/src/Search/Search.svelte b/sveltejs/src/Search/Search.svelte
index ece982f63a9fd58a297a4cacac66231ee87c850a..9e8d01f2ed288d7e8aa70e4105a7bd85baa3cc67 100644
--- a/sveltejs/src/Search/Search.svelte
+++ b/sveltejs/src/Search/Search.svelte
@@ -7,7 +7,6 @@
   import SearchSort from './SearchSort.svelte';
   import {
     filters,
-    rowsCount,
     filtersVocabularies,
     moduleCategoryFilter,
     moduleCategoryVocabularies,
@@ -28,10 +27,10 @@
   // cspell:ignore searchterm
 
   const { Drupal } = window;
-  const { announce } = Drupal;
   const dispatch = createEventDispatcher();
   const stateContext = getContext('state');
 
+  export let refreshLiveRegion;
   export const filter = (row, text) =>
     Object.values(row).filter(
       (item) =>
@@ -55,33 +54,6 @@
   }
   let sortText = sortMatch.text;
 
-  /**
-   * Refreshes the live region after a filter or search completes.
-   */
-  const refreshLiveRegion = () => {
-    if ($rowsCount) {
-      // Set announce() to an empty string. This ensures the result count will
-      // be announced after filtering even if the count is the same.
-      announce('');
-
-      // The announcement is delayed by 210 milliseconds, a wait that is
-      // slightly longer than the 200 millisecond debounce() built into
-      // announce(). This ensures that the above call to reset the aria live
-      // region to an empty string actually takes place instead of being
-      // debounced.
-      setTimeout(() => {
-        announce(
-          Drupal.t('@count Results, Sorted by @sortText', {
-            '@count': $rowsCount
-              .toString()
-              .replace(/\B(?=(\d{3})+(?!\d))/g, ','),
-            '@sortText': sortText,
-          }),
-        );
-      }, 210);
-    }
-  };
-
   const updateVocabularies = (vocabulary, value) => {
     const normalizedValue = normalizeOptions(value);
     const storedValue = JSON.parse(localStorage.getItem(`pb.${vocabulary}`));
@@ -97,7 +69,7 @@
     updateVocabularies('securityCoverage', SECURITY_OPTIONS);
   });
 
-  async function onSearch(event) {
+  export async function onSearch(event) {
     const state = stateContext.getState();
     const detail = {
       originalEvent: event,
@@ -219,14 +191,6 @@
   >
     <section aria-label={Drupal.t('Search results')}>
       <div class="search__results-count">
-        <span id="output">
-          {$rowsCount &&
-            $rowsCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
-          {Drupal.t('Results')}
-          <span class="visually-hidden"
-            >{Drupal.t('Sorted by @sortText', { '@sortText': sortText })}</span
-          >
-        </span>
         {#each ['developmentStatus', 'maintenanceStatus', 'securityCoverage'] as filterType}
           {#if $filters[filterType]}
             <FilterApplied
@@ -335,16 +299,6 @@
     z-index: 1;
   }
 
-  #output {
-    display: inline-block;
-    font-family: sans-serif;
-    font-style: normal;
-    font-weight: 700;
-    font-size: 14px;
-    line-height: 21px;
-    margin-inline-start: 20px;
-  }
-
   .search__grid-container {
     display: grid;
     height: auto;
diff --git a/sveltejs/src/constants.js b/sveltejs/src/constants.js
index d977be40835410710028cfc4d660e78d9f6fad37..7f204cdd6651450b3b01c7034f0682cf510ee2dc 100644
--- a/sveltejs/src/constants.js
+++ b/sveltejs/src/constants.js
@@ -22,3 +22,4 @@ export const DARK_COLOR_SCHEME =
   matchMedia('(forced-colors: active)').matches &&
   matchMedia('(prefers-color-scheme: dark)').matches;
 export const PM_VALIDATION_ERROR = drupalSettings.project_browser.pm_validation;
+export const ACTIVE_PLUGINS = drupalSettings.project_browser.active_plugins;
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
index 3941545856adbb7d822c8ecec7f7c7937c252fdf..47df202a2ae245463d4e68765ae3931699a491ba 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
@@ -154,8 +154,8 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->svelteInitHelper('text', 'E-commerce');
 
     // Click 'E-commerce' category on module page.
-    $this->clickWithWait('#project-browser li:nth-child(2)');
-    $module_category_e_commerce_filter_selector = 'p.filter-applied:nth-child(4)';
+    $this->clickWithWait('li.module-page__category-list-item:nth-child(2)');
+    $module_category_e_commerce_filter_selector = 'p.filter-applied:nth-child(3)';
     $this->assertEquals('E-commerce', $this->getElementText("$module_category_e_commerce_filter_selector .filter-applied__label"));
     $this->assertTrue($assert_session->waitForText('6 Results'));
   }
@@ -173,7 +173,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     // Click 'E-commerce' checkbox.
     $this->clickWithWait('#104');
 
-    $module_category_e_commerce_filter_selector = 'p.filter-applied:nth-child(4)';
+    $module_category_e_commerce_filter_selector = 'p.filter-applied:nth-child(3)';
     // Make sure the 'E-commerce' module category filter is applied.
     $this->assertEquals('E-commerce', $this->getElementText("$module_category_e_commerce_filter_selector .filter-applied__label"));
 
@@ -201,7 +201,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->clickWithWait('#55');
 
     // Make sure the 'Media' module category filter is applied.
-    $this->assertEquals('Media', $this->getElementText('p.filter-applied:nth-child(3) .filter-applied__label'));
+    $this->assertEquals('Media', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
     // Assert that only media and administration module categories are shown.
     $this->assertProjectsVisible([
       'Jazz',
@@ -387,7 +387,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
       'Astronaut Simulator',
     ]);
 
-    $second_filter_selector = 'p.filter-applied:nth-child(3)';
+    $second_filter_selector = 'p.filter-applied:nth-child(2)';
     // Make sure the second filter applied is the security covered filter.
     $this->assertEquals('Covered by a security policy', $this->getElementText("$second_filter_selector .filter-applied__label"));
 
@@ -420,7 +420,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->clickWithWait('#developmentStatusactive');
 
     // Make sure the correct filter was applied.
-    $this->assertEquals('Active', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
+    $this->assertEquals('Active', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
 
     $this->assertProjectsVisible([
       'Jazz',
@@ -459,7 +459,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
 
     // Click the Actively maintained filter.
     $this->clickWithWait('#maintenanceStatusmaintained');
-    $this->assertEquals('Maintained', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
+    $this->assertEquals('Maintained', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
 
     $this->assertProjectsVisible([
       'Jazz',
@@ -704,9 +704,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     ]);
     $this->assertTrue($assert_session->waitForText('16 Results'));
 
-    $this->assertEquals('Active', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
-    $this->assertEquals('Commerce/Advertising', $this->getElementText('p.filter-applied:nth-child(3) .filter-applied__label'));
-    $this->assertEquals('Media', $this->getElementText('p.filter-applied:nth-child(4) .filter-applied__label'));
+    $this->assertEquals('Active', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
+    $this->assertEquals('Commerce/Advertising', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
+    $this->assertEquals('Media', $this->getElementText('p.filter-applied:nth-child(3) .filter-applied__label'));
 
     $this->clickWithWait('[aria-label="First page"]');
     $this->assertProjectsVisible([
@@ -724,9 +724,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
       'Cream cheese on a bagel',
     ]);
 
-    $this->assertEquals('Active', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
-    $this->assertEquals('Commerce/Advertising', $this->getElementText('p.filter-applied:nth-child(3) .filter-applied__label'));
-    $this->assertEquals('Media', $this->getElementText('p.filter-applied:nth-child(4) .filter-applied__label'));
+    $this->assertEquals('Active', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
+    $this->assertEquals('Commerce/Advertising', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
+    $this->assertEquals('Media', $this->getElementText('p.filter-applied:nth-child(3) .filter-applied__label'));
   }
 
   /**
@@ -741,9 +741,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->pressWithWait('Recommended filters');
 
     // Check that the actively maintained tag is present.
-    $this->assertEquals('Maintained', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
+    $this->assertEquals('Maintained', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
     // Make sure the second filter applied is the security covered filter.
-    $this->assertEquals('Covered by a security policy', $this->getElementText('p.filter-applied:nth-child(3) .filter-applied__label'));
+    $this->assertEquals('Covered by a security policy', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
     $this->assertTrue($assert_session->waitForText('9 Results'));
   }
 
@@ -765,7 +765,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $tab_count = $page->findAll('css', '.project-browser__plugin-tabs button');
     $this->assertCount(2, $tab_count);
     // Get result count for first tab.
-    $this->assertEquals('9 Results Sorted by Active installs', $this->getElementText('#output'));
+    $this->assertEquals('9 Results', $this->getElementText('.search-results'));
 
     // Apply filters in drupalorg_mockapi(first tab).
     $assert_session->waitForElement('css', '.views-exposed-form__item input[type="checkbox"]');
@@ -793,16 +793,16 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->svelteInitHelper('css', '.filter__checkbox');
     $assert_session->elementsCount('css', '.filter__checkbox', 20);
     $assert_session->waitForElementVisible('css', '#project-browser .project');
-    $this->assertNotEquals('9 Results Sorted by Active installs', $this->getElementText('.search__results-count #output'));
+    $this->assertNotEquals('9 Results Sorted by Active installs', $this->getElementText('.search-results'));
     $assert_session->waitForElementVisible('css', '#project-browser .project');
-    $result_count_text = $page->find('css', '.search__results-count #output')->getText();
+    $result_count_text = $page->find('css', '.search-results')->getText();
     $this->assertNotEquals('9 Results Sorted by Active installs', $result_count_text);
     // Apply the second module category filter.
     $second_category_filter_selector = '#project-browser > div.project-browser__container > .project-browser__aside > div > form > section > details > fieldset > label:nth-child(3)';
     $this->clickWithWait("$second_category_filter_selector");
 
     // Save the filter applied in second tab.
-    $applied_filter = $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label');
+    $applied_filter = $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label');
     // Save the number of results.
     $results_before = count($page->findAll('css', '#project-browser .project.list'));
 
@@ -810,9 +810,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $page->pressButton('drupalorg_mockapi');
     // Assert that the filters persist.
     $this->assertTrue($assert_session->waitForText('4 Results'));
-    $first_filter_element = $page->find('css', 'p.filter-applied:nth-child(2)');
+    $first_filter_element = $page->find('css', 'p.filter-applied:nth-child(1)');
     $this->assertEquals('E-commerce', $first_filter_element->find('css', '.filter-applied__label')->getText());
-    $second_filter_element = $page->find('css', 'p.filter-applied:nth-child(3)');
+    $second_filter_element = $page->find('css', 'p.filter-applied:nth-child(2)');
     $this->assertEquals('Media', $second_filter_element->find('css', '.filter-applied__label')->getText());
     $this->assertProjectsVisible([
       'Tooth Fairy',
@@ -824,7 +824,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     // Again switch to second tab.
     $page->pressButton('random_data');
     // Assert that the filters persist.
-    $this->assertEquals($applied_filter, $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
+    $this->assertEquals($applied_filter, $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
 
     // Assert that the number of results is the same.
     $results_after = count($page->findAll('css', '#project-browser .project.list'));