diff --git a/css/pb.css b/css/pb.css
index 7b8f865c2e8717365f631bc80576670a6ba2301b..9336c78a8c16fb2f08832e3a1a92dc978d71f5b1 100644
--- a/css/pb.css
+++ b/css/pb.css
@@ -5,9 +5,6 @@
   margin: 0;
   padding: 0;
 }
-.pb-filter__heading--narrow {
-  margin-block-start: 0;
-}
 .pb-filter__summary {
   padding: 0;
   cursor: pointer;
@@ -15,27 +12,57 @@
 .pb-filter__summary--open {
   padding-bottom: 0.95rem;
 }
-.pb-filter__heading {
-  display: inline;
-  margin-top: 0;
-  padding: 0 0.5rem;
-  font-size: 1rem;
-}
 .pb-filter__checkbox-label {
-  display: block;
-  padding: 5px 0;
+  display: flex;
+  align-items: center;
+  gap: 1rem;
+  padding-left: 1rem;
+  border-bottom: 1px solid rgba(212, 212, 218, 0.6);
 }
-.pb-filter__checkbox {
-  margin-inline: 10px;
+.pb-filter__checkbox-label:hover,
+.pb-filter__checkbox-label:focus {
+  background-color: #f3f4f9;
 }
+
+.pb-filter__checkbox-label-txt {
+  display: block;
+  flex-grow: 1;
+  padding: 1rem 0;
+  cursor: pointer;
+}
+
 .pb-filter__fieldset {
   border: none;
 }
-.pb-filter__checkbox-label,
-.pb-filter__checkbox {
+.pb-filter__multi-dropdown {
+  min-width: 10rem;
+}
+.pb-filter__multi-dropdown__label {
+  position: relative;
+  display: inline-block;
   cursor: pointer;
 }
 
+.pb-filter__multi-dropdown {
+  position: relative;
+}
+
+.pb-filter__multi-dropdown__items--hidden {
+  display: none;
+}
+.pb-filter__multi-dropdown__items--visible {
+  position: absolute;
+  z-index: 100;
+  top: 47px;
+  right: 0;
+  display: block;
+  overflow: scroll;
+  width: 100%;
+  max-height: 380px;
+  background-color: #fff;
+  box-shadow: 0 0.5rem 0.5rem 0 rgba(0, 0, 0, 0.1);
+}
+
 /* <ImageCarousel> */
 .pb-image-carousel {
   display: flex;
@@ -171,7 +198,6 @@
 .pb-layout__header {
   display: flex;
   justify-content: space-between;
-  border-bottom: 1px solid #dee2e6;
 }
 .pb-search-results {
   margin-bottom: 5px;
@@ -243,7 +269,6 @@
 .pb-layout {
   display: flex;
   flex-flow: column nowrap;
-  gap: 1.5rem;
 }
 
 @media screen and (min-width: 800px) {
@@ -609,24 +634,65 @@
 }
 
 /* <Search/Search> */
-.search__form-item {
-  margin-top: 0;
+.search__form-container {
+  margin: -1px 0 2rem;
+  padding: 1.5rem;
+  border: 1px solid #dedfe4;
+  border-radius: 1px;
+  background-color: #fff;
+  box-shadow: 0 2px 0.25rem rgba(0, 0, 0, 0.1);
+}
+
+@media screen and (min-width: 600px) {
+  .search__form-filters {
+    display: grid;
+    grid-template-columns: repeat(6, 1fr);
+    align-items: center;
+    gap: 1rem;
+  }
+
+  .search__form-filters .search__bar-container {
+    grid-column: span 6;
+  }
+
+  .search__form-filters .form-type--select,
+  .search__form-filters .filter-group__filter-options {
+    grid-column: span 3;
+  }
 }
-.search__form {
-  display: inherit;
-  flex-wrap: wrap;
-  margin-top: 2.375rem;
-  padding: 0 0 1.5rem;
+
+@media screen and (min-width: 1200px) {
+  .search__form-filters {
+    grid-template-columns: repeat(8, 1fr);
+  }
+
+  .search__form-filters .search__bar-container {
+    grid-column: span 8;
+  }
+
+  .search__form-filters .form-type--select,
+  .search__form-filters .filter-group__filter-options {
+    grid-column: span 2;
+  }
+}
+
+.search__form-filters .form-element--type-select {
+  width: 100%;
+}
+
+.search__bar-container.form-item {
+  margin-block-end: 1.5rem;
 }
 
 .search__search-bar .search__search_term {
-  position: relative;
-  display: flex;
   width: 100%;
-  height: 50px;
+  padding: 0 3rem 0 1rem;
+  border: none;
   outline: none;
 }
-
+.search__filter-select {
+  min-width: 10rem;
+}
 .search__search_term::-webkit-search-cancel-button,
 .search__search_term::-webkit-search-decoration,
 .search__search_term::-webkit-search-results-button,
@@ -636,29 +702,35 @@
 
 .search__search-bar {
   position: relative;
-  height: 50px;
+  min-width: 10rem;
   cursor: pointer;
-  text-align: center;
   color: #fff;
   border: 1px solid #919297;
   border-radius: 2px;
+  background-color: #fff;
   font-size: 20px;
 }
 
+.search__search-bar:hover {
+  border-color: #000;
+}
+
 .search__search-icon {
   position: absolute;
+  z-index: 1;
+  right: 10px;
   bottom: 12px;
-  inset-inline-end: 30px;
 }
 
 .search__search-clear {
   position: absolute;
+  z-index: 1;
   top: 0;
+  right: 40px;
   height: 100%;
   cursor: pointer;
   border: none;
   background: none;
-  inset-inline-end: 60px;
 }
 
 .search__search_term::placeholder {
@@ -677,15 +749,19 @@
   border: 3px solid #f3f4f9;
 }
 
-.search__grid-container {
-  display: grid;
-  grid-template-columns: 5fr auto auto;
-  grid-gap: 20px;
-  align-items: center;
-  max-width: 100%;
-  height: auto;
-  padding: 5px;
-  background: #f3f4f9;
+.search__form-filters-container {
+  padding: 1rem 1.5rem;
+  background-color: #f3f4f9;
+}
+
+@media screen and (min-width: 800px) {
+  .search__form-sort {
+    display: grid;
+    grid-template-columns: 5fr auto;
+    grid-gap: 1rem;
+    align-items: center;
+    padding-top: 1rem;
+  }
 }
 
 .search__filter-button {
@@ -697,12 +773,6 @@
   background: none;
 }
 
-@media screen and (max-width: 855px) {
-  .search__grid-container {
-    display: block;
-  }
-}
-
 /* <Search/SearchFilters> */
 .search__filters {
   display: flex;
@@ -718,9 +788,10 @@
   margin-inline-end: 1em;
 }
 
-.search__filter-wrapper {
+.search__filters {
   display: flex;
   align-items: center;
+  padding: 0.5rem 0;
 }
 
 .search__filter__toggle.form-element {
@@ -763,7 +834,10 @@
 .search__sort {
   z-index: 2;
 }
-
+.search__sort label {
+  font-size: 0.9rem;
+  font-weight: bold;
+}
 .search__sort-select.form-element {
   border: none;
   background-color: #d3d4d9;
diff --git a/sveltejs/public/build/bundle.js b/sveltejs/public/build/bundle.js
index 45fe7eac640958ab74619e3ad8eede5e3da73758..77726f38845aab0952ba9f3b9f7640a3e39b1a8f 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 98f6940379942eb891ddfd46236c3d3f825a0565..b8b9abd3aeb50472487df518895306cb44580c54 100644
Binary files a/sveltejs/public/build/bundle.js.map and b/sveltejs/public/build/bundle.js.map differ
diff --git a/sveltejs/src/Filter.svelte b/sveltejs/src/Filter.svelte
index 9ad1d58025f66389f091513dc1dfcd0b2e79e3a3..a53a6e2b344c6b2e6202ad3d1c2d7b2de580de83 100644
--- a/sveltejs/src/Filter.svelte
+++ b/sveltejs/src/Filter.svelte
@@ -5,13 +5,103 @@
     moduleCategoryVocabularies,
     activeTab,
   } from './stores';
-  import MediaQuery from './MediaQuery.svelte';
   import { normalizeOptions, shallowCompare } from './util';
 
   const { Drupal } = window;
   const dispatch = createEventDispatcher();
   const stateContext = getContext('state');
 
+  let filterVisible = false;
+
+  function showHideFilter() {
+    filterVisible = !filterVisible;
+    if (filterVisible) {
+      setTimeout(() => {
+        document
+          .getElementsByClassName('pb-filter__checkbox-label')[0]
+          .firstElementChild.focus();
+      }, 50);
+      return;
+    }
+    document
+      .getElementsByClassName('pb-filter__multi-dropdown__items')[0]
+      .focus();
+  }
+
+  function onBlur(event) {
+    if (
+      event.relatedTarget === null ||
+      !document
+        .getElementsByClassName('pb-filter__multi-dropdown')[0]
+        .contains(event.relatedTarget)
+    ) {
+      filterVisible = false;
+    }
+  }
+
+  function onKeyDown(event) {
+    // Space to open category filter drop-down.
+    if (
+      event.key === ' ' &&
+      event.target.classList.contains('pb-filter__multi-dropdown')
+    ) {
+      showHideFilter();
+      event.preventDefault();
+      return;
+    }
+    // Alt Up/Down opens/closes category filter drop-down.
+    if (
+      event.altKey &&
+      (event.key === 'ArrowDown' || event.key === 'ArrowUp')
+    ) {
+      showHideFilter();
+      event.preventDefault();
+      return;
+    }
+    // Down arrow on checkbox moves to next checkbox.
+    if (
+      event.target.classList.contains('pb-filter__checkbox') &&
+      event.key === 'ArrowDown' &&
+      event.target.parentElement.nextElementSibling !== null
+    ) {
+      event.target.parentElement.nextElementSibling.firstElementChild.focus();
+      event.preventDefault();
+      return;
+    }
+    // Up arrow on checkbox moves to previous checkbox.
+    if (
+      event.target.classList.contains('pb-filter__checkbox') &&
+      event.key === 'ArrowUp' &&
+      event.target.parentElement.previousElementSibling !== null
+    ) {
+      event.target.parentElement.previousElementSibling.firstElementChild.focus();
+      event.preventDefault();
+      return;
+    }
+    // Tab moves off filter.
+    if (event.key === 'Tab') {
+      if (event.shiftKey) {
+        // Shift+tab moves to search box.
+        document.getElementById('pb-text').focus();
+        event.preventDefault();
+        return;
+      }
+      // Tab without shift moves to next filter.
+      document.getElementsByName('securityCoverage')[0].focus();
+      event.preventDefault();
+      return;
+    }
+
+    // Escape closes filter drop-down.
+    if (
+      event.target.classList.contains('pb-filter__checkbox') &&
+      event.key === 'Escape'
+    ) {
+      filterVisible = false;
+      document.getElementsByClassName('pb-filter__multi-dropdown')[0].focus();
+    }
+  }
+
   async function onSelectCategory(event) {
     const state = stateContext.getState();
     const detail = {
@@ -25,6 +115,12 @@
     dispatch('selectCategory', detail);
     stateContext.setPage(0, 0);
     stateContext.setRows(detail.rows);
+    filterVisible = true;
+    if (event.target.classList.contains('pb-filter__checkbox')) {
+      setTimeout(() => {
+        event.target.focus();
+      }, 50);
+    }
   }
 
   async function fetchAllCategories() {
@@ -54,46 +150,57 @@
   });
 </script>
 
-<MediaQuery query="(min-width: 800px)" let:matches>
-  <form class="pb-filter">
-    <section aria-label={Drupal.t('Filter categories')}>
-      <details
-        class="pb-filter__categories"
-        class:pb-filter__categories--open={matches}
-        open={matches}
+<section
+  aria-label={Drupal.t('Filter by category')}
+  class="search__form-item js-form-item form-item js-form-type-select form-type--select"
+>
+  <fieldset class="pb-filter__fieldset">
+    <label for="pb-text" class="form-item__label"
+      >{Drupal.t('Filter by category')}</label
+    >
+    {#await apiModuleCategory then categoryList}
+      <div
+        role="button"
+        tabindex="0"
+        class="pb-filter__multi-dropdown form-element form-element--type-select"
+        on:click={() => {
+          showHideFilter();
+        }}
+        on:blur={onBlur}
+        on:keydown={onKeyDown}
       >
-        <summary
-          class="pb-filter__summary"
-          class:pb-filter__summary--open={matches}
-          hidden={matches}
+        <span class="pb-filter__multi-dropdown__label"
+          >{$moduleCategoryFilter.length
+            ? $moduleCategoryFilter
+                .map((category) => $moduleCategoryVocabularies[category])
+                .join(', ')
+            : Drupal.t('Select categories')}</span
+        >
+        <div
+          class="pb-filter__multi-dropdown__items
+        pb-filter__multi-dropdown__items--{filterVisible
+            ? 'visible'
+            : 'hidden'}"
         >
-          <h2 class="pb-filter__heading pb-filter__heading--wide">
-            {Drupal.t('Filter Categories')}
-          </h2>
-        </summary>
-        <fieldset class="pb-filter__fieldset">
-          <h2
-            class="pb-filter__heading pb-filter__heading--narrow"
-            class:visually-hidden={!matches}
-          >
-            {Drupal.t('Filter Categories')}
-          </h2>
-          {#await apiModuleCategory then categoryList}
-            {#each categoryList[$activeTab] as dt}
-              <label class="pb-filter__checkbox-label">
-                <input
-                  type="checkbox"
-                  id={dt.id}
-                  class="pb-filter__checkbox"
-                  bind:group={$moduleCategoryFilter}
-                  on:change={onSelectCategory}
-                  value={dt.id}
-                />{dt.name}</label
-              >
-            {/each}
-          {/await}
-        </fieldset>
-      </details>
-    </section>
-  </form>
-</MediaQuery>
+          {#each categoryList[$activeTab] as dt}
+            <div class="pb-filter__checkbox-label">
+              <input
+                type="checkbox"
+                id={dt.id}
+                class="pb-filter__checkbox form-checkbox form-boolean form-boolean--type-checkbox"
+                bind:group={$moduleCategoryFilter}
+                on:change={onSelectCategory}
+                on:blur={onBlur}
+                on:keydown={onKeyDown}
+                value={dt.id}
+              />
+              <label for={dt.id} class="pb-filter__checkbox-label-txt">
+                {dt.name}
+              </label>
+            </div>
+          {/each}
+        </div>
+      </div>
+    {/await}
+  </fieldset>
+</section>
diff --git a/sveltejs/src/ProjectBrowser.svelte b/sveltejs/src/ProjectBrowser.svelte
index ed881acb0730070eeb003a24eb89b017bca24712..8bf58aa2f575c2b59a300ba0885ea8631ec9b5e0 100644
--- a/sveltejs/src/ProjectBrowser.svelte
+++ b/sveltejs/src/ProjectBrowser.svelte
@@ -1,7 +1,7 @@
 <script>
   import { onMount } from 'svelte';
   import { withPrevious } from 'svelte-previous';
-  import ProjectGrid, { Search, Filter } from './ProjectGrid.svelte';
+  import ProjectGrid, { Search } from './ProjectGrid.svelte';
   import Pagination from './Pagination.svelte';
   import Project from './Project/Project.svelte';
   import Tabs from './Tabs.svelte';
@@ -61,7 +61,6 @@
   focusedElement.subscribe((value) => {
     element = value;
   });
-  let filterComponent;
   let searchComponent;
 
   /**
@@ -247,7 +246,8 @@
       .forEach((t) => t.setAttribute('aria-selected', false));
     // Set this tab as selected
     target.setAttribute('aria-selected', true);
-    filterComponent.setModuleCategoryVocabulary();
+    // @TODO this needs to get ported into Search somehow:
+    // filterComponent.setModuleCategoryVocabulary();
     $categoryCheckedTrack[$activeTab] = $moduleCategoryFilter;
     $moduleCategoryFilter = [];
     $activeTab = event.detail.pluginId;
@@ -385,12 +385,6 @@
       </div>
     </div>
 
-    <div slot="left">
-      <Filter
-        on:selectCategory={onSelectCategory}
-        bind:this={filterComponent}
-      />
-    </div>
     {#each rows as row, index (row)}
       <Project {toggleView} project={row} />
     {/each}
diff --git a/sveltejs/src/Search/FilterGroup.svelte b/sveltejs/src/Search/FilterGroup.svelte
index 620b71b3060aa2d1d7f3c4b4f7b20d73352137fe..ea75622adc86915994f7a867aadd7ee0a5421363 100644
--- a/sveltejs/src/Search/FilterGroup.svelte
+++ b/sveltejs/src/Search/FilterGroup.svelte
@@ -7,30 +7,16 @@
   export let filterType;
 </script>
 
-<fieldset class="filter-group">
-  <legend class="filter-group__title-wrapper">
-    {filterTitle}:
-  </legend>
-  <div class="filter-group__filter-options-wrapper">
-    <div class="filter-group__filter-options">
-      {#each Object.entries(filterData) as [id, label]}
-        <div class="filter-group__filter-option">
-          <input
-            type="radio"
-            name={filterType}
-            id={filterType + id}
-            class="filter-group__radio form-radio form-boolean form-boolean--type-radio"
-            bind:group={$filters[filterType]}
-            on:change={changeHandler}
-            value={id}
-          />
-          <slot class="filter-group__label-slot" name="label" {id} {label}>
-            <label class="filter-group__option-label" for={filterType + id}>
-              {label}
-            </label>
-          </slot>
-        </div>
-      {/each}
-    </div>
-  </div>
-</fieldset>
+<div class="filter-group__filter-options form-item">
+  <label for={filterType} class="form-item__label">{filterTitle}</label>
+  <select
+    name={filterType}
+    class="search__filter-select form-select form-element form-element--type-select"
+    bind:value={$filters[filterType]}
+    on:change={changeHandler}
+  >
+    {#each Object.entries(filterData) as [id, label]}
+      <option value={id}>{label}</option>
+    {/each}
+  </select>
+</div>
diff --git a/sveltejs/src/Search/Search.svelte b/sveltejs/src/Search/Search.svelte
index 59639be63f521a2ed83b51d59dac072c7545ac60..9f846b1c26f7d076c6a1143a578319791438fd3a 100644
--- a/sveltejs/src/Search/Search.svelte
+++ b/sveltejs/src/Search/Search.svelte
@@ -1,9 +1,9 @@
 <script>
   import { createEventDispatcher, getContext, onMount } from 'svelte';
   import FilterApplied from './FilterApplied.svelte';
+  import FilterGroup from './FilterGroup.svelte';
   import { normalizeOptions, shallowCompare } from '../util';
-  import SearchFilters from './SearchFilters.svelte';
-  import SearchFilterToggle from './SearchFilterToggle.svelte';
+  import { Filter } from '../ProjectGrid.svelte';
   import SearchSort from './SearchSort.svelte';
   import {
     filters,
@@ -44,14 +44,13 @@
     placeholder: Drupal.t('Module Name, Keyword(s), etc.'),
   };
 
-  // eslint-disable-next-line prefer-const
-  let filtersOpen = false;
   let sortMatch = $sortCriteria.find((option) => option.id === $sort);
   if (typeof sortMatch === 'undefined') {
     $sort = $sortCriteria[0].id;
     sortMatch = $sortCriteria.find((option) => option.id === $sort);
   }
   let sortText = sortMatch.text;
+  let filterComponent;
 
   const updateVocabularies = (vocabulary, value) => {
     const normalizedValue = normalizeOptions(value);
@@ -130,12 +129,6 @@
     stateContext.setRows(detail.rows);
   }
 
-  function removeFilter(filterType) {
-    $filters[filterType] = ALL_VALUES_ID;
-    $filters = $filters;
-    onAdvancedFilter();
-  }
-
   function clearText() {
     $searchString = '';
     onSearch();
@@ -162,13 +155,13 @@
   };
 </script>
 
-<form class="search__form">
+<form class="search__form-container">
   <div
-    class="search__form-item js-form-item form-item js-form-type-textfield form-type--textfield"
+    class="search__bar-container search__form-item js-form-item form-item js-form-type-textfield form-type--textfield"
     role="search"
   >
     <label for="pb-text" class="form-item__label"
-      >{Drupal.t('Search for modules')}</label
+      >{Drupal.t('Keyword search')}</label
     >
     <div class="search__search-bar">
       <input
@@ -214,65 +207,96 @@
       />
     </div>
   </div>
-  <div
-    class="search__grid-container js-form-item js-form-type-select form-type--select js-form-item-type form-item--type"
-  >
-    <section
-      class="search__filter-wrapper"
-      aria-label={Drupal.t('Search results')}
+  <div class="search__form-filters-container">
+    <div class="search__form-filters">
+      <Filter
+        on:selectCategory={onSelectCategory}
+        bind:this={filterComponent}
+      />
+      <FilterGroup
+        filterTitle={Drupal.t('Security Advisory Coverage')}
+        filterData={SECURITY_OPTIONS}
+        filterType="securityCoverage"
+        changeHandler={onAdvancedFilter}
+        let:id
+        let:label
+      />
+      <FilterGroup
+        filterTitle={Drupal.t('Maintenance Status')}
+        filterData={MAINTENANCE_OPTIONS}
+        filterType="maintenanceStatus"
+        changeHandler={onAdvancedFilter}
+        let:id
+        let:label
+      >
+        <label
+          slot="label"
+          class="search__checkbox-label"
+          for={`maintenanceStatus${id}`}
+        >
+          {label}
+        </label>
+      </FilterGroup>
+      <FilterGroup
+        filterTitle={Drupal.t('Development Status')}
+        filterData={DEVELOPMENT_OPTIONS}
+        filterType="developmentStatus"
+        changeHandler={onAdvancedFilter}
+        let:id
+        let:label
+      >
+        <label
+          slot="label"
+          class="search__checkbox-label"
+          for={`developmentStatus${id}`}
+        >
+          {label}
+        </label>
+      </FilterGroup>
+    </div>
+    <div
+      class="search__form-sort js-form-item js-form-type-select form-type--select js-form-item-type form-item--type"
     >
-      <SearchFilterToggle bind:isOpen={filtersOpen} />
-      <div class="search__results-count">
-        {#each ['developmentStatus', 'maintenanceStatus', 'securityCoverage'] as filterType}
-          {#if $filters[filterType]}
+      <section class="search__filters" aria-label={Drupal.t('Search results')}>
+        <div class="search__results-count">
+          {#each $moduleCategoryFilter as category}
             <FilterApplied
-              id={$filters[filterType]}
-              label={$filtersVocabularies[filterType][$filters[filterType]]}
-              clickHandler={() => removeFilter(filterType)}
+              id={category}
+              label={$moduleCategoryVocabularies[category]}
+              clickHandler={() => {
+                $moduleCategoryFilter.splice(
+                  $moduleCategoryFilter.indexOf(category),
+                  1,
+                );
+                $moduleCategoryFilter = $moduleCategoryFilter;
+                onSelectCategory();
+              }}
             />
-          {/if}
-        {/each}
-
-        {#each $moduleCategoryFilter as category}
-          <FilterApplied
-            id={category}
-            label={$moduleCategoryVocabularies[category]}
-            clickHandler={() => {
-              $moduleCategoryFilter.splice(
-                $moduleCategoryFilter.indexOf(category),
-                1,
-              );
-              $moduleCategoryFilter = $moduleCategoryFilter;
-              onSelectCategory();
-            }}
-          />
-        {/each}
+          {/each}
 
-        {#if $filters.securityCoverage !== ALL_VALUES_ID || $filters.maintenanceStatus !== ALL_VALUES_ID || $filters.developmentStatus !== ALL_VALUES_ID || $moduleCategoryFilter.length}
-          <button
-            class="search__filter-button"
-            type="button"
-            on:click|preventDefault={() =>
-              filterResets(ALL_VALUES_ID, ALL_VALUES_ID, ALL_VALUES_ID)}
-          >
-            {Drupal.t('Clear filters')}
-          </button>
-        {/if}
-        {#if !($filters.maintenanceStatus === ACTIVELY_MAINTAINED_ID && $filters.securityCoverage === COVERED_ID && $filters.developmentStatus === ALL_VALUES_ID && $moduleCategoryFilter.length === 0)}
-          <button
-            class="search__filter-button"
-            type="button"
-            on:click|preventDefault={() =>
-              filterResets(ACTIVELY_MAINTAINED_ID, ALL_VALUES_ID, COVERED_ID)}
-          >
-            {Drupal.t('Recommended filters')}
-          </button>
-        {/if}
-      </div>
-    </section>
-    <SearchSort on:sort bind:sortText refresh={refreshLiveRegion} />
-  </div>
-  <div class="search__dropdown dropdown-filters" id="filter-dropdown">
-    <SearchFilters bind:isOpen={filtersOpen} {onAdvancedFilter} />
+          {#if $filters.securityCoverage !== ALL_VALUES_ID || $filters.maintenanceStatus !== ALL_VALUES_ID || $filters.developmentStatus !== ALL_VALUES_ID || $moduleCategoryFilter.length}
+            <button
+              class="search__filter-button"
+              type="button"
+              on:click|preventDefault={() =>
+                filterResets(ALL_VALUES_ID, ALL_VALUES_ID, ALL_VALUES_ID)}
+            >
+              {Drupal.t('Clear filters')}
+            </button>
+          {/if}
+          {#if !($filters.maintenanceStatus === ACTIVELY_MAINTAINED_ID && $filters.securityCoverage === COVERED_ID && $filters.developmentStatus === ALL_VALUES_ID && $moduleCategoryFilter.length === 0)}
+            <button
+              class="search__filter-button"
+              type="button"
+              on:click|preventDefault={() =>
+                filterResets(ACTIVELY_MAINTAINED_ID, ALL_VALUES_ID, COVERED_ID)}
+            >
+              {Drupal.t('Recommended filters')}
+            </button>
+          {/if}
+        </div>
+      </section>
+      <SearchSort on:sort bind:sortText refresh={refreshLiveRegion} />
+    </div>
   </div>
 </form>
diff --git a/sveltejs/src/Search/SearchFilterToggle.svelte b/sveltejs/src/Search/SearchFilterToggle.svelte
deleted file mode 100644
index 35d33a5082500f098001933be9a6876934ab5813..0000000000000000000000000000000000000000
--- a/sveltejs/src/Search/SearchFilterToggle.svelte
+++ /dev/null
@@ -1,33 +0,0 @@
-<script>
-  import { FULL_MODULE_PATH } from '../constants';
-
-  const { Drupal } = window;
-
-  // eslint-disable-next-line import/prefer-default-export
-  export let isOpen;
-
-  /* When the user clicks on the button,
- toggle between hiding and showing the dropdown content */
-  function openDropdown() {
-    isOpen = !isOpen;
-  }
-</script>
-
-<div class="search__filter__toggle-container">
-  <section aria-label={Drupal.t('Filter settings')}>
-    <button
-      type="button"
-      class="search__filter__toggle form-element"
-      class:is_open={isOpen}
-      aria-controls="filter-dropdown"
-      aria-label={isOpen ? Drupal.t('Close Filter') : Drupal.t('Open Filter')}
-      aria-expanded={isOpen.toString()}
-      on:click={() => openDropdown()}
-      ><img
-        src="{FULL_MODULE_PATH}/images/advanced-filter-icon.svg"
-        alt="advanced filter icon"
-        class="search__filter__toggle-img"
-      />Filters
-    </button>
-  </section>
-</div>
diff --git a/sveltejs/src/Search/SearchFilters.svelte b/sveltejs/src/Search/SearchFilters.svelte
deleted file mode 100644
index 3570db9cd064db295116931c5f1a6e4fd1dde7fa..0000000000000000000000000000000000000000
--- a/sveltejs/src/Search/SearchFilters.svelte
+++ /dev/null
@@ -1,71 +0,0 @@
-<script>
-  import { slide } from 'svelte/transition';
-  import FilterGroup from './FilterGroup.svelte';
-  import {
-    COVERED_ID,
-    MAINTENANCE_OPTIONS,
-    DEVELOPMENT_OPTIONS,
-    SECURITY_OPTIONS,
-  } from '../constants';
-
-  const { Drupal } = window;
-
-  export let onAdvancedFilter;
-  export let isOpen;
-</script>
-
-{#if isOpen}
-  <div class="search__filters" transition:slide>
-    <FilterGroup
-      filterTitle={Drupal.t('Development Status')}
-      filterData={DEVELOPMENT_OPTIONS}
-      filterType="developmentStatus"
-      changeHandler={onAdvancedFilter}
-      let:id
-      let:label
-    >
-      <label
-        slot="label"
-        class="search__checkbox-label"
-        for={`developmentStatus${id}`}
-      >
-        {label}
-      </label>
-    </FilterGroup>
-    <FilterGroup
-      filterTitle={Drupal.t('Maintenance Status')}
-      filterData={MAINTENANCE_OPTIONS}
-      filterType="maintenanceStatus"
-      changeHandler={onAdvancedFilter}
-      let:id
-      let:label
-    >
-      <label
-        slot="label"
-        class="search__checkbox-label"
-        for={`maintenanceStatus${id}`}
-      >
-        {label}
-      </label>
-    </FilterGroup>
-    <FilterGroup
-      filterTitle={Drupal.t('Security Advisory Coverage')}
-      filterData={SECURITY_OPTIONS}
-      filterType="securityCoverage"
-      changeHandler={onAdvancedFilter}
-      let:id
-      let:label
-    >
-      <label
-        slot="label"
-        class="search__checkbox-label"
-        for={`securityCoverage${id}`}
-      >
-        {label}
-        {#if id === COVERED_ID}
-          <span class="small-icons" />
-        {/if}
-      </label>
-    </FilterGroup>
-  </div>
-{/if}
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
index 6d86b5deb91cef0db83773fff8386f6256eea7eb..1c875f9c811fceac02cea2d7dcbfbc0dfa33fcc5 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
@@ -15,6 +15,14 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
 
   use ProjectBrowserUiTestTrait;
 
+  // Could be moved into trait under PHP 8.3.
+  protected const SECURITY_OPTION_SELECTOR = 'select[name="securityCoverage"] ';
+  protected const MAINTENANCE_OPTION_SELECTOR = 'select[name="maintenanceStatus"] ';
+  protected const DEVELOPMENT_OPTION_SELECTOR = 'select[name="developmentStatus"] ';
+  protected const OPTION_CHECKED = 'option:checked';
+  protected const OPTION_FIRST_CHILD = 'option:first-child';
+  protected const OPTION_LAST_CHILD = 'option:last-child';
+
   /**
    * {@inheritdoc}
    */
@@ -97,24 +105,29 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
     $this->drupalGet('admin/modules/browse');
     $this->svelteInitHelper('text', 'Results');
 
-    // Make sure the second filter applied is the security covered filter.
-    $this->assertEquals('Covered by a security policy', $this->getElementText('p.filter-applied:last-of-type .filter-applied__label'));
-
-    $this->openAdvancedFilter();
-    $security_radio_option_selector = '.filter-group:last-child input:checked ~ label';
-    $maintenance_radio_option_selector = '.filter-group:nth-child(2) input:checked ~ label';
-    $assert_session->waitForElementVisible('css', $security_radio_option_selector);
-    $this->assertEquals(trim('Covered by a security policy'), $this->getElementText($security_radio_option_selector));
-    $this->assertEquals('Maintained', $this->getElementText($maintenance_radio_option_selector));
+    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show all', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     // Clear the security covered filter.
-    $this->clickWithWait("p.filter-applied:last-of-type > button");
-    $this->assertEquals('Show all', $this->getElementText($security_radio_option_selector));
+    $this->clickWithWait(self::SECURITY_OPTION_SELECTOR . self::OPTION_LAST_CHILD);
+    $this->assertEquals('Show all', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
+
+    // Set the development status filter.
+    $this->clickWithWait(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_FIRST_CHILD);
+    $this->assertEquals('Active', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     // Clear all filters.
     $this->pressWithWait('Clear filters');
-    $this->assertEquals('Show all', $this->getElementText($security_radio_option_selector));
-    $this->assertEquals('Show all', $this->getElementText($security_radio_option_selector));
+    $this->assertEquals('Show all', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show all', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show all', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
+
+    // Reset to recommended filters.
+    $this->pressWithWait('Recommended filters');
+    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
+    $this->assertEquals('Show all', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
   }
 
   /**
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
index 688b4dc1a09f240dda4ff4cf94b07f4078d2a40d..1b9adabd63e176d9849fbf7cca09660a594cd006 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
@@ -26,6 +26,14 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
 
   use ProjectBrowserUiTestTrait;
 
+  // Could be moved into trait under PHP 8.3.
+  protected const SECURITY_OPTION_SELECTOR = 'select[name="securityCoverage"] ';
+  protected const MAINTENANCE_OPTION_SELECTOR = 'select[name="maintenanceStatus"] ';
+  protected const DEVELOPMENT_OPTION_SELECTOR = 'select[name="developmentStatus"] ';
+  protected const OPTION_CHECKED = 'option:checked';
+  protected const OPTION_FIRST_CHILD = 'option:first-child';
+  protected const OPTION_LAST_CHILD = 'option:last-child';
+
   /**
    * {@inheritdoc}
    */
@@ -94,7 +102,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
   }
 
   /**
-   * Tests the clickable category functionality.
+   * Tests the clickable category functionality on module page.
    */
   public function testClickableCategory(): void {
     $assert_session = $this->assertSession();
@@ -102,13 +110,15 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
 
     $this->drupalGet('admin/modules/browse');
     $this->svelteInitHelper('text', 'Dancing Queen');
+
+    // Click to open module page.
     $page->clickLink('Dancing Queen');
-    $this->svelteInitHelper('text', 'E-commerce');
+    $this->svelteInitHelper('text', 'Categories:');
 
-    // Click 'E-commerce' category on module page.
+    // Click 'E-commerce' lozenge category on module page.
     $this->clickWithWait('li.pb-module-page__categories-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"));
+    $module_category_first_filter_selector = 'p.filter-applied:first-child .filter-applied__label';
+    $this->assertEquals('E-commerce', $this->getElementText($module_category_first_filter_selector));
     $this->assertTrue($assert_session->waitForText('6 Results'));
   }
 
@@ -120,12 +130,14 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $assert_session = $this->assertSession();
 
     $this->drupalGet('admin/modules/browse');
-    $this->svelteInitHelper('css', '#104');
+    $this->svelteInitHelper('css', '.pb-filter__multi-dropdown');
+    // Open category drop-down.
+    $this->clickWithWait('.pb-filter__multi-dropdown', 'E-commerce');
 
     // Click 'E-commerce' checkbox.
     $this->clickWithWait('#104');
 
-    $module_category_e_commerce_filter_selector = 'p.filter-applied:nth-child(3)';
+    $module_category_e_commerce_filter_selector = 'p.filter-applied:first-child';
     // 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"));
 
@@ -144,8 +156,15 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
       'Astronaut Simulator',
     ], TRUE);
 
+    // Use blur event to close drop-down so Clear is visible.
+    $this->assertSession()->elementExists('css', '.pb-filter__multi-dropdown')->blur();
+
     $this->pressWithWait('Clear filters', '25 Results');
 
+    // Open category drop-down again by pressing space.
+    $this->assertSession()->elementExists('css', '.pb-filter__multi-dropdown')->keyDown(' ');
+    $this->assertSession()->waitForText('Media');
+
     // Click 'Media' checkbox.
     $this->clickWithWait('#67');
 
@@ -286,6 +305,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     // shown on a page, the pager has the correct number of items.
     $this->clickWithWait('[aria-label="First page"]');
 
+    // Open category drop-down.
+    $this->clickWithWait('.pb-filter__multi-dropdown', 'E-commerce');
+
     // Click 'Media' checkbox.
     $this->clickWithWait('#67', '', TRUE);
 
@@ -342,12 +364,11 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
       'Astronaut Simulator',
     ]);
 
-    $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"));
+    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     // Clear the security covered filter.
-    $this->clickWithWait("$second_filter_selector > button");
+    $this->clickWithWait('select[name="securityCoverage"] option:last-child');
     $this->assertProjectsVisible([
       'Jazz',
       'Vitamin&C;$?',
@@ -363,19 +384,16 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
       'Helvetica',
     ]);
 
-    $this->openAdvancedFilter();
-
     // Check aria-labelledby property for advanced filter.
     foreach ($page->findAll('css', '.filters [role="group"]') as $element) {
       $this->assertSame($element->findAll('xpath', 'div')[0]->getAttribute('id'), $element->getAttribute('aria-labelledby'));
     }
 
     // Click the Active filter.
-    $assert_session->waitForElementVisible('css', '#developmentStatusactive');
-    $this->clickWithWait('#developmentStatusactive');
+    $this->clickWithWait(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_FIRST_CHILD);
 
     // Make sure the correct filter was applied.
-    $this->assertEquals('Active', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
+    $this->assertEquals('Active', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     $this->assertProjectsVisible([
       'Jazz',
@@ -393,7 +411,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     ]);
 
     // Click the "Show all" filter for security.
-    $this->clickWithWait('#securityCoverageall', '', TRUE);
+    $this->clickWithWait(self::SECURITY_OPTION_SELECTOR . self::OPTION_LAST_CHILD, '', TRUE);
     $this->assertProjectsVisible([
       'Jazz',
       'Cream cheese on a bagel',
@@ -413,8 +431,8 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->pressWithWait('Clear filters', '25 Results');
 
     // Click the Actively maintained filter.
-    $this->clickWithWait('#maintenanceStatusmaintained');
-    $this->assertEquals('Maintained', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
+    $this->clickWithWait(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_FIRST_CHILD);
+    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     $this->assertProjectsVisible([
       'Jazz',
@@ -605,14 +623,15 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->svelteInitHelper('text', 'Clear Filters');
     $this->pressWithWait('Clear filters');
 
-    $this->openAdvancedFilter();
-
     // Select 'Z-A' sorting order.
     $this->sortBy('z_a');
 
     // Select the active development status filter.
-    $assert_session->waitForElementVisible('css', '#developmentStatusactive');
-    $this->clickWithWait('#developmentStatusactive');
+    $assert_session->waitForElementVisible('css', self::DEVELOPMENT_OPTION_SELECTOR);
+    $this->clickWithWait(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_FIRST_CHILD);
+
+    // Open category drop-down.
+    $this->clickWithWait('.pb-filter__multi-dropdown', 'E-commerce');
 
     // Select the E-commerce filter.
     $assert_session->waitForElementVisible('css', '#104');
@@ -654,9 +673,8 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     ]);
     $this->assertTrue($assert_session->waitForText('15 Results'));
 
-    $this->assertEquals('Active', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
-    $this->assertEquals('E-commerce', $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->assertEquals('E-commerce', $this->getElementText('p.filter-applied:first-child .filter-applied__label'));
+    $this->assertEquals('Media', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
 
     $this->clickWithWait('[aria-label="First page"]');
     $this->assertProjectsVisible([
@@ -674,9 +692,8 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
       'Become a Banana',
     ], TRUE);
 
-    $this->assertEquals('Active', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
-    $this->assertEquals('E-commerce', $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->assertEquals('E-commerce', $this->getElementText('p.filter-applied:first-child .filter-applied__label'));
+    $this->assertEquals('Media', $this->getElementText('p.filter-applied:nth-child(2) .filter-applied__label'));
   }
 
   /**
@@ -691,9 +708,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(1) .filter-applied__label'));
+    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
     // 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(2) .filter-applied__label'));
+    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
     $this->assertTrue($assert_session->waitForText('9 Results'));
   }
 
@@ -722,12 +739,18 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
 
     $this->pressWithWait('Clear filters', '25 Results');
 
+    // Open category drop-down.
+    $this->clickWithWait('.pb-filter__multi-dropdown', 'E-commerce', TRUE);
+
     // Click 'E-commerce' checkbox.
     $this->clickWithWait('#104');
 
     // Click 'Media' checkbox.
     $this->clickWithWait('#67', '20 Results');
 
+    // Use blur event to close drop-down so Clear is visible.
+    $this->assertSession()->elementExists('css', '.pb-filter__multi-dropdown')->blur();
+
     // Filter by search text.
     $this->inputSearchField('Number');
     $this->assertTrue($assert_session->waitForText('2 Results'));
@@ -745,8 +768,12 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $assert_session->waitForElementVisible('css', '#project-browser .pb-project');
     $result_count_text = $page->find('css', '.pb-search-results')->getText();
     $this->assertNotEquals('9 Results Sorted by Active installs', $result_count_text);
+
+    // Open category drop-down again by pressing space.
+    $this->assertSession()->elementExists('css', '.pb-filter__multi-dropdown')->keyDown(' ');
+
     // Apply the second module category filter.
-    $second_category_filter_selector = '#project-browser > div.pb-layout > .pb-layout__aside > div > form > section > details > fieldset > label:nth-child(3)';
+    $second_category_filter_selector = '.pb-filter__multi-dropdown__items > .pb-filter__checkbox-label:nth-child(3) input';
     $this->clickWithWait("$second_category_filter_selector");
 
     // Save the filter applied in second tab.
@@ -928,6 +955,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
       'Astronaut Simulator',
     ]);
 
+    // Open category drop-down.
+    $this->clickWithWait('.pb-filter__multi-dropdown', 'E-commerce', TRUE);
+
     // Click 'Media' checkbox.
     $this->clickWithWait('#67');
     $this->assertPagerItems(['1', '2', 'Next', 'Last']);
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
index bd2649e17805182a2e2257751c56f931949cdafb..32e0c8dc1ebe626fb74989416c22ce67356df354 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
@@ -19,6 +19,14 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
 
   use ProjectBrowserUiTestTrait;
 
+  // Could be moved into trait under PHP 8.3.
+  protected const SECURITY_OPTION_SELECTOR = 'select[name="securityCoverage"] ';
+  protected const MAINTENANCE_OPTION_SELECTOR = 'select[name="maintenanceStatus"] ';
+  protected const DEVELOPMENT_OPTION_SELECTOR = 'select[name="developmentStatus"] ';
+  protected const OPTION_CHECKED = 'option:checked';
+  protected const OPTION_FIRST_CHILD = 'option:first-child';
+  protected const OPTION_LAST_CHILD = 'option:last-child';
+
   /**
    * {@inheritdoc}
    */
@@ -80,8 +88,8 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     $assert_session = $this->assertSession();
 
     $this->drupalGet('admin/modules/browse');
-    $this->svelteInitHelper('css', '.pb-filter__categories input[type="checkbox"]');
-    $assert_session->elementsCount('css', '.pb-filter__categories input[type="checkbox"]', 54);
+    $this->svelteInitHelper('css', '.pb-filter__multi-dropdown input[type="checkbox"]');
+    $assert_session->elementsCount('css', '.pb-filter__multi-dropdown input[type="checkbox"]', 54);
   }
 
   /**
@@ -98,7 +106,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
 
     // Click 'Utility' category on module page.
     $this->clickWithWait('.pb-module-page__details .pb-module-page__categories-list li:nth-child(2)');
-    $module_category_utility_filter_selector = 'p.filter-applied:nth-child(3)';
+    $module_category_utility_filter_selector = 'p.filter-applied:nth-child(1)';
     $this->assertEquals('Utility', $this->getElementText("$module_category_utility_filter_selector .filter-applied__label"));
     $this->assertTrue($assert_session->waitForText('732 Results'));
   }
@@ -110,12 +118,18 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     $assert_session = $this->assertSession();
 
     $this->drupalGet('admin/modules/browse');
-    $this->svelteInitHelper('css', '#acc38507-ac85-43e6-8f32-beb3febea93f');
+    $this->svelteInitHelper('css', '.pb-filter__multi-dropdown');
+    // Open category drop-down.
+    $this->clickWithWait('.pb-filter__multi-dropdown', 'E-commerce', TRUE);
 
+    $this->svelteInitHelper('css', '#acc38507-ac85-43e6-8f32-beb3febea93f');
     // Click 'E-commerce' checkbox.
     $this->clickWithWait('#acc38507-ac85-43e6-8f32-beb3febea93f');
 
-    $module_category_e_commerce_filter_selector = 'p.filter-applied:nth-child(3)';
+    // Use blur event to close drop-down so Clear is visible.
+    $this->assertSession()->elementExists('css', '.pb-filter__multi-dropdown')->blur();
+
+    $module_category_e_commerce_filter_selector = 'p.filter-applied:nth-child(1)';
     // 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"));
 
@@ -131,6 +145,9 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
 
     $this->pressWithWait('Clear filters', '197 Results');
 
+    // Open category drop-down.
+    $this->clickWithWait('.pb-filter__multi-dropdown', 'E-commerce', TRUE);
+
     // Click 'Media' checkbox.
     $this->clickWithWait('#ee0200ec-4920-411e-9768-2cc588deaa38');
 
@@ -200,6 +217,9 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     // shown on a page, the pager has the correct number of items.
     $this->clickWithWait('[aria-label="First page"]');
 
+    // Open category drop-down.
+    $this->clickWithWait('.pb-filter__multi-dropdown', 'E-commerce');
+
     // Click 'Commerce/Advertising' checkbox.
     $this->clickWithWait('#23d470f6-ffde-4034-a6ef-492b7121b9cf', '', TRUE);
 
@@ -225,32 +245,29 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
       'Chaos Tool Suite (ctools)', 'Token', 'Pathauto', 'Libraries API', 'Entity API', 'Webform', 'Metatag', 'Field Group', 'IMCE', 'CAPTCHA', 'Google Analytics', 'Redirect',
     ]);
 
-    $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"));
+    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     // Clear the security covered filter.
-    $this->clickWithWait("$second_filter_selector > button");
+    $this->clickWithWait(self::SECURITY_OPTION_SELECTOR . self::OPTION_LAST_CHILD);
     $this->assertProjectsVisible([
       'Chaos Tool Suite (ctools)', 'Token', 'Pathauto', 'Libraries API', 'Entity API', 'Webform', 'Metatag', 'Field Group', 'IMCE', 'CAPTCHA', 'Google Analytics', 'Redirect',
     ]);
 
-    $this->openAdvancedFilter();
-
     // Click the Active filter.
-    $assert_session->waitForElementVisible('css', '#developmentStatusactive');
-    $this->clickWithWait('#developmentStatusactive');
+    $assert_session->waitForElementVisible('css', self::DEVELOPMENT_OPTION_SELECTOR);
+    $this->clickWithWait(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_FIRST_CHILD);
 
     // Make sure the correct filter was applied.
-    $this->assertEquals('Active', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
+    $this->assertEquals('Active', $this->getElementText(self::DEVELOPMENT_OPTION_SELECTOR . self::OPTION_CHECKED));
     $assert_session->waitForText('No records available');
 
     // Clear all filters.
     $this->pressWithWait('Clear filters', 'Results');
 
     // Click the Actively maintained filter.
-    $this->clickWithWait('#maintenanceStatusmaintained', '6,749 Results');
-    $this->assertEquals('Maintained', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
+    $this->clickWithWait(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_FIRST_CHILD, '6,749 Results');
+    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
 
     $this->assertProjectsVisible([
       'Chaos Tool Suite (ctools)', 'Token', 'Pathauto', 'Libraries API', 'Entity API', 'Webform', 'Metatag', 'Field Group', 'IMCE', 'CAPTCHA', 'Google Analytics', 'Redirect',
@@ -338,9 +355,9 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     $this->pressWithWait('Recommended filters');
 
     // Check that the actively maintained tag is present.
-    $this->assertEquals('Maintained', $this->getElementText('p.filter-applied:nth-child(1) .filter-applied__label'));
+    $this->assertEquals('Maintained', $this->getElementText(self::MAINTENANCE_OPTION_SELECTOR . self::OPTION_CHECKED));
     // 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(2) .filter-applied__label'));
+    $this->assertEquals('Covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
     $this->assertTrue($assert_session->waitForText('4,523 Results'));
   }
 
@@ -354,8 +371,8 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     $this->container->get('module_installer')->install(['project_browser_devel']);
     // Test categories with multiple plugin enabled.
     $this->drupalGet('admin/modules/browse');
-    $this->svelteInitHelper('css', '.pb-filter__categories input[type="checkbox"]');
-    $assert_session->elementsCount('css', '.pb-filter__categories input[type="checkbox"]', 54);
+    $this->svelteInitHelper('css', '.pb-filter__multi-dropdown input[type="checkbox"]');
+    $assert_session->elementsCount('css', '.pb-filter__multi-dropdown input[type="checkbox"]', 54);
 
     $this->svelteInitHelper('css', '#project-browser .pb-project');
     // Count tabs.
@@ -369,12 +386,18 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
 
     $this->pressWithWait('Clear filters', '7,236 Results');
 
+    // Open category drop-down.
+    $this->clickWithWait('.pb-filter__multi-dropdown', 'E-commerce', TRUE);
+
     // Click 'E-commerce' checkbox.
     $this->clickWithWait('#acc38507-ac85-43e6-8f32-beb3febea93f');
 
     // Click 'Commerce/Advertising' checkbox.
     $this->clickWithWait('#23d470f6-ffde-4034-a6ef-492b7121b9cf', '557 Results');
 
+    // Use blur event to close drop-down so Clear is visible.
+    $this->assertSession()->elementExists('css', '.pb-filter__multi-dropdown')->blur();
+
     // Filter by search text.
     $this->inputSearchField('th');
     $this->assertTrue($assert_session->waitForText('2 Results'));
@@ -384,12 +407,16 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
 
     // Click other tab.
     $this->pressWithWait('random_data');
-    $this->svelteInitHelper('css', '.pb-filter__categories input[type="checkbox"]');
-    $assert_session->elementsCount('css', '.pb-filter__categories input[type="checkbox"]', 20);
     $assert_session->waitForElementVisible('css', '#project-browser .pb-project');
 
+    // Open category drop-down.
+    $this->clickWithWait('.pb-filter__multi-dropdown', '', TRUE);
+
+    $this->svelteInitHelper('css', '.pb-filter__multi-dropdown input[type="checkbox"]');
+    $assert_session->elementsCount('css', '.pb-filter__multi-dropdown input[type="checkbox"]', 20);
+
     // Apply the second module category filter.
-    $second_category_filter_selector = '#project-browser > div.pb-layout > .pb-layout__aside > div > form > section > details > fieldset > label:nth-child(2)';
+    $second_category_filter_selector = '.pb-filter__multi-dropdown .pb-filter__checkbox-label:nth-child(2) input[type="checkbox"]';
     $this->clickWithWait("$second_category_filter_selector");
 
     // Save the filter applied in second tab.
@@ -490,8 +517,8 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
 
     // Verify that only Random data plugin is enabled.
     $this->drupalGet('admin/modules/browse');
-    $this->svelteInitHelper('css', '.pb-filter__categories input[type="checkbox"]');
-    $assert_session->elementsCount('css', '.pb-filter__categories input[type="checkbox"]', 20);
+    $this->svelteInitHelper('css', '.pb-filter__multi-dropdown input[type="checkbox"]');
+    $assert_session->elementsCount('css', '.pb-filter__multi-dropdown input[type="checkbox"]', 20);
 
     // Enable only Drupal.org mockapi plugin through config update. It is done
     // this way because dragging was not working reliably for enabling
@@ -503,8 +530,8 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
 
     // Verify that only Drupal.org mockapi plugin is enabled.
     $this->drupalGet('admin/modules/browse');
-    $this->svelteInitHelper('css', '.pb-filter__categories input[type="checkbox"]');
-    $assert_session->elementsCount('css', '.pb-filter__categories input[type="checkbox"]', 19);
+    $this->svelteInitHelper('css', '.pb-filter__multi-dropdown input[type="checkbox"]');
+    $assert_session->elementsCount('css', '.pb-filter__multi-dropdown input[type="checkbox"]', 19);
   }
 
 }
diff --git a/tests/src/FunctionalJavascript/TranslatedSvelteAppTest.php b/tests/src/FunctionalJavascript/TranslatedSvelteAppTest.php
index 6420418ca5c82548c4ab9fdf7f497bae196f2d1c..005ad4ef6cb6c5ce1fde5f9b91c0886854bbb211 100644
--- a/tests/src/FunctionalJavascript/TranslatedSvelteAppTest.php
+++ b/tests/src/FunctionalJavascript/TranslatedSvelteAppTest.php
@@ -71,7 +71,7 @@ class TranslatedSvelteAppTest extends WebDriverTestBase {
     $translate_to = 'Soorch Foor Moodools';
 
     $this->drupalGet('admin/modules/browse');
-    $this->svelteInitHelper('text', 'Search for modules');
+    $this->svelteInitHelper('text', 'Keyword search');
     $this->assertFalse($this->assertSession()->waitForText($translate_to));
 
     // This forces locale JS string sources to be imported.
@@ -80,12 +80,12 @@ class TranslatedSvelteAppTest extends WebDriverTestBase {
     // Translate a string in locale.admin.js to our new language.
     $strings = \Drupal::service('locale.storage')
       ->getStrings([
-        'source' => 'Search for modules',
+        'source' => 'Keyword search',
       ]);
 
     $string = $strings[0];
 
-    $this->submitForm(['string' => 'Search for modules'], 'Filter');
+    $this->submitForm(['string' => 'Keyword search'], 'Filter');
     $edit = ['strings[' . $string->lid . '][translations][0]' => $translate_to];
     $this->submitForm($edit, 'Save translations');
     $this->drupalGet("/$prefix/admin/modules/browse");
diff --git a/tests/src/Nightwatch/Tests/keyboardTest.js b/tests/src/Nightwatch/Tests/keyboardTest.js
new file mode 100644
index 0000000000000000000000000000000000000000..16e220f8a61f19713397c738c99d5ff50bb64389
--- /dev/null
+++ b/tests/src/Nightwatch/Tests/keyboardTest.js
@@ -0,0 +1,208 @@
+const delayInMilliseconds = 100;
+const filterKeywordSearch = '#pb-text';
+const filterDropdownSelector = '.pb-filter__multi-dropdown';
+
+module.exports = {
+  '@tags': ['project_browser'],
+  before(browser) {
+    browser
+      .drupalInstall()
+      .drupalInstallModule('project_browser')
+      .drupalInstallModule('project_browser_test');
+  },
+  after(browser) {
+    browser.drupalUninstall();
+  },
+  'Project browser categories': (browser) => {
+    const assertFocus = (selector, message) => {
+      browser.execute(
+        // eslint-disable-next-line func-names, prefer-arrow-callback, no-shadow
+        function (selector) {
+          return document.activeElement.matches(selector);
+        },
+        [selector],
+        (result) => {
+          browser.assert.ok(result.value, message);
+        },
+      );
+    };
+    function sendDownKey() {
+      return this.actions().sendKeys(browser.Keys.ARROW_DOWN);
+    }
+    browser.drupalLoginAsAdmin(() => {
+      // Open project browser settings page, enable mock API, and disable
+      // drupal.org and recipes.
+      browser
+        .drupalRelativeURL('/admin/config/development/project_browser')
+        .waitForElementVisible('#edit-enabled-sources-drupalorg-mockapi-status')
+        .updateValue(
+          '[data-drupal-selector="edit-enabled-sources-drupalorg-mockapi-status"]',
+          'enabled',
+        )
+        .updateValue(
+          '[data-drupal-selector="edit-enabled-sources-drupalorg-jsonapi-status"]',
+          'disabled',
+        )
+        // Check for presence of Recipe option, as won't exist for < Drupal 10.
+        .element(
+          'css selector',
+          '#edit-enabled-sources-recipes-status',
+          function disableRecipes(result) {
+            if (result.status !== -1) {
+              browser.updateValue(
+                '[data-drupal-selector="edit-enabled-sources-recipes-status"]',
+                'disabled',
+              );
+            }
+          },
+        )
+        .click('[data-drupal-selector="edit-submit"]')
+        .waitForElementVisible('[data-drupal-messages]')
+        .assert.textContains(
+          '[data-drupal-messages]',
+          'The configuration options have been saved',
+        );
+
+      // Open project browser.
+      browser
+        .drupalRelativeURL('/admin/modules/browse')
+        .waitForElementVisible('h1', delayInMilliseconds)
+        .assert.textContains('h1', 'Browse projects')
+        .waitForElementVisible(filterDropdownSelector);
+
+      // Use mouse to get to search box, and verify it has active focus.
+      browser.click(filterKeywordSearch);
+      assertFocus(filterKeywordSearch, 'Assert search box has focus.');
+
+      // Press tab to navigate to categories drop-down.
+      browser
+        .keys(browser.Keys.TAB)
+        .pause(delayInMilliseconds)
+        .assert.textEquals(
+          '.pb-filter__multi-dropdown__label',
+          'Select categories',
+          'Assert that drop-down label has initial select message.',
+        )
+        .assert.not.visible(
+          '.pb-filter__multi-dropdown__items',
+          'Assert that category checkboxes are hidden.',
+        );
+      assertFocus(
+        filterDropdownSelector,
+        'Assert that focus moves to category drop-down on tab.',
+      );
+
+      // Press space to expand categories drop-down, verify focus moves to first
+      // checkbox control.
+      browser
+        .keys(browser.Keys.SPACE)
+        .assert.visible(
+          '.pb-filter__multi-dropdown__items',
+          'Assert category checkboxes are now visible.',
+        )
+        .pause(1000);
+      assertFocus(
+        '.pb-filter__checkbox-label:first-child .pb-filter__checkbox',
+        'Assert that first category checkbox has focus.',
+      );
+
+      // Press down arrow key, verify focus moves to next checkbox.
+      browser.perform(sendDownKey).pause(delayInMilliseconds);
+      assertFocus(
+        '.pb-filter__checkbox-label:nth-child(2) .pb-filter__checkbox',
+        'Assert that second category checkbox has focus.',
+      );
+
+      // Press space key. Verify active checkbox checked.
+      browser
+        .keys(browser.Keys.SPACE)
+        .pause(delayInMilliseconds)
+        .assert.selected(
+          '.pb-filter__checkbox-label:nth-child(2) .pb-filter__checkbox',
+          'Assert second category is selected.',
+        );
+
+      // Press escape key. Verify category drop-down closes and focus goes to
+      // overall drop-down.
+      browser
+        .keys(browser.Keys.ESCAPE)
+        .pause(delayInMilliseconds)
+        .assert.not.visible(
+          '.pb-filter__checkbox',
+          'Assert category checkboxes are hidden again.',
+        )
+        .assert.textEquals(
+          '.pb-filter__multi-dropdown__label',
+          'Accessibility',
+          'Assert that Accessibility on drop-down label.',
+        );
+      assertFocus(
+        filterDropdownSelector,
+        'Assert that focus is back on drop-down list.',
+      );
+
+      // Verify Accessibility lozenge shown.
+      browser.assert.textEquals(
+        '.filter-applied:first-child .filter-applied__label',
+        'Accessibility',
+        'Assert that Accessibility lozenge is shown.',
+      );
+
+      // Press space to expand categories drop-down again.
+      browser
+        .keys(browser.Keys.SPACE)
+        .pause(delayInMilliseconds)
+        .assert.visible(
+          '.pb-filter__multi-dropdown__items',
+          'Assert category checkboxes are now visible.',
+        );
+      assertFocus(
+        '.pb-filter__checkbox-label:first-child .pb-filter__checkbox',
+        'Assert that first category checkbox has focus.',
+      );
+
+      // Press down arrow key to move to second checkbox.
+      browser.perform(sendDownKey).pause(delayInMilliseconds);
+      assertFocus(
+        '.pb-filter__checkbox-label:nth-child(2) .pb-filter__checkbox',
+        'Assert that second category checkbox has focus.',
+      );
+
+      // Press space key. Verify checkbox cleared.
+      browser
+        .keys(browser.Keys.SPACE)
+        .pause(delayInMilliseconds)
+        .assert.not.selected(
+          '.pb-filter__checkbox-label:nth-child(2) .pb-filter__checkbox',
+          'Assert second category is selected.',
+        );
+
+      // Press space to expand categories drop-down again.
+      browser
+        .keys(browser.Keys.TAB)
+        .pause(delayInMilliseconds)
+        .assert.textEquals(
+          '.pb-filter__multi-dropdown__label',
+          'Select categories',
+          'Assert that drop-down label has initial select message.',
+        )
+        .assert.not.visible(
+          '.pb-filter__checkbox',
+          'Assert category checkboxes are hidden again.',
+        );
+      assertFocus(
+        '.filter-group__filter-options:first-of-type .search__filter-select',
+        'Assert that focus has moved to next filter drop-down.',
+      );
+
+      // Verify Accessibility lozenge shown.
+      browser.assert.not.elementPresent(
+        '.filter-applied:first-child .filter-applied__label',
+        'Assert that no filter lozenge.',
+      );
+
+      // Close out test.
+      browser.drupalLogAndEnd({ onlyOnError: false });
+    });
+  },
+};