diff --git a/drupalci.yml b/drupalci.yml
index 971fd83b6e8bc52857c74a53e107f2e01ec9e8de..7e32b3c2f8a98f88146a56d3a36651b3d7889b85 100644
--- a/drupalci.yml
+++ b/drupalci.yml
@@ -3,7 +3,7 @@
 build:
   assessment:
     validate_codebase: {}
-      # Project Browser code quality checking matches that of Drupal core: it is checked by container_command.commit_checks.
+    # Project Browser code quality checking matches that of Drupal core: it is checked by container_command.commit_checks.
     testing:
       # Run code quality checks.
       container_command.commit-checks:
@@ -29,12 +29,14 @@ build:
           - sed -i "s/core\/assets\/vendor/sveltejs\/public\/build/" modules/contrib/project_browser/commit-code-check.sh
           # Stop not requiring ES6 files for core's Nightwatch tests, instead don't for *our* Nightwatch tests.
           - sed -i "s/\^core\/tests\/Drupal\/Nightwatch/\^tests\/src\/Nightwatch/" modules/contrib/project_browser/commit-code-check.sh
-          - sed -i "s/.eslintrc.passing.json/DISABLED.eslintrc.passing.json/" modules/contrib/project_browser/commit-code-check.sh
+          # Remove check for changed .js files to avoid running core eslint. There is svelte specific
+          # linting run instead.
+          - sed -i "s/FILE =~.*\.js\$ ]]; then/FILE =~ \\.noopjs$ ]]; then/" modules/contrib/project_browser/commit-code-check.sh
           # Disable default JS compile checks, PB will be doing a hacky version
           - sed -i "s/COMPILE_CHECK=1/COMPILE_CHECK=0/" modules/contrib/project_browser/commit-code-check.sh
           - sed -i "s/core\/scripts\/js/\(js|scripts|modules|project_browser_plugin_starter_template\)/" modules/contrib/project_browser/commit-code-check.sh
           - sed -i "s/\^core\/scripts\/css/config.js\$/" modules/contrib/project_browser/commit-code-check.sh
-            # Run eslint on .svelte files.
+          # Run eslint on .svelte files.
           - sed -i '/.*FINAL_STATUS" == "1".*/i cd "$TOP_LEVEL\/sveltejs"' modules/contrib/project_browser/commit-code-check.sh
           - sed -i '/.*FINAL_STATUS" == "1".*/i yarn install' modules/contrib/project_browser/commit-code-check.sh
           - sed -i '/.*FINAL_STATUS" == "1".*/i yarn lint:svelte' modules/contrib/project_browser/commit-code-check.sh
diff --git a/sveltejs/public/build/bundle.css b/sveltejs/public/build/bundle.css
index a8f11ae896f8c66d59bbec6bf160d3cf2965fe1c..a294b9daea92e4db146bf6306386e836ae68ba9d 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 2656adba1c79f67fcdf6a4039f1f19473a18d9b5..5a783ff35d90447604d4d37f4625636699ef7712 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 99f13659a63de835c252a13bcde9aaa09ffc3566..e9d7d53a6334548e82d378e60ca083290d250a20 100644
Binary files a/sveltejs/public/build/bundle.js.map and b/sveltejs/public/build/bundle.js.map differ
diff --git a/sveltejs/public/index.html b/sveltejs/public/index.html
index 1a9838e76d2786536119dd09d10ad79de96b0fe5..f515e22053bfe70fb31d60486a40d35f45ad1f25 100644
--- a/sveltejs/public/index.html
+++ b/sveltejs/public/index.html
@@ -11,7 +11,7 @@
 	<script defer src='/build/bundle.js'></script>
 </head>
 <body>
-<div id="project-browser">
+<div id="project-browser" class="project-browser">
 </div>
 </body>
 </html>
diff --git a/sveltejs/src/ModulePage.svelte b/sveltejs/src/ModulePage.svelte
index 711ad1a20d177f336bfb4c83effad76a23e74b90..07997953e733ba82574ecc2e981a4c4b6e0f5922 100644
--- a/sveltejs/src/ModulePage.svelte
+++ b/sveltejs/src/ModulePage.svelte
@@ -51,8 +51,8 @@
         <ul class="module-page__category-list" aria-labelledby="categories">
           {#each project.module_categories || [] as category}
             <li
-              on:click={filterByCategory(category.id)}
-              class="module-page__category-list-item on"
+              on:click={() => filterByCategory(category.id)}
+              class="module-page__category-list-item"
             >
               {category.name}
             </li>
@@ -109,10 +109,7 @@
         <ImageCarousel sources={project.project_images} />
       </div>
     {/if}
-    <div
-      class="module-page__wrapper__box-2--project-description"
-      id="description-wrapper"
-    >
+    <div class="module-page__project-description" id="description-wrapper">
       {@html project.body.value}
     </div>
   </div>
@@ -167,7 +164,7 @@
     height: 20px;
     cursor: pointer;
   }
-  .module-page__category-list-item.on {
+  .module-page__category-list-item {
     list-style: none;
     display: inline-block;
     margin-top: 5px;
diff --git a/sveltejs/src/Project/ProjectButtonBase.svelte b/sveltejs/src/Project/ProjectButtonBase.svelte
index 92d3ec06eaa4265d76ca6edef0a15b8c1da59dd1..0d4bb4fd88eeb70eeb26c2994aa850913c20c66d 100644
--- a/sveltejs/src/Project/ProjectButtonBase.svelte
+++ b/sveltejs/src/Project/ProjectButtonBase.svelte
@@ -1,12 +1,12 @@
 <script>
   // eslint-disable-next-line import/prefer-default-export
-  export let click;
+  export let click = () => {};
 </script>
 
 <button
+  on:click={click}
   class="button button--primary project_button--primary"
   {...$$restProps}
-  on:click={click}
 >
   <slot />
 </button>
diff --git a/sveltejs/src/ProjectBrowser.svelte b/sveltejs/src/ProjectBrowser.svelte
index 5d91d92ff514e852d67831c2946b3a46b0911860..b6b621902dfcbd412dc1dd680f886e0aa32cc8fc 100644
--- a/sveltejs/src/ProjectBrowser.svelte
+++ b/sveltejs/src/ProjectBrowser.svelte
@@ -247,14 +247,7 @@
 </script>
 
 <MediaQuery query="(min-width: 1200px)" let:matches>
-  <ProjectGrid
-    class="project-browser"
-    {loading}
-    {rows}
-    {pageIndex}
-    {$pageSize}
-    let:rows
-  >
+  <ProjectGrid {loading} {rows} {pageIndex} {$pageSize} let:rows>
     <div slot="top">
       <Search
         on:search={onSearch}
@@ -335,9 +328,7 @@
       {/if}
       <Pagination
         page={$page}
-        {$pageSize}
         count={$rowsCount}
-        serverSide={true}
         on:pageChange={onPageChange}
         on:pageSizeChange={onPageSizeChange}
       />
@@ -355,9 +346,7 @@
     <div slot="bottom">
       <Pagination
         page={$page}
-        {$pageSize}
         count={$rowsCount}
-        serverSide={true}
         on:pageChange={onPageChange}
         on:pageSizeChange={onPageSizeChange}
       />
diff --git a/sveltejs/src/ProjectGrid.svelte b/sveltejs/src/ProjectGrid.svelte
index 7ed56c76cb349be53a755adbb2e8db3ea8f828c1..b3c023ceb325570e396d34ac341fd8bb5272aad8 100644
--- a/sveltejs/src/ProjectGrid.svelte
+++ b/sveltejs/src/ProjectGrid.svelte
@@ -1,5 +1,5 @@
 <script context="module">
-  import Search from './Search.svelte';
+  import Search from './Search/Search.svelte';
   import Filter from './Filter.svelte';
   import Loading from './Loading.svelte';
   import { pageSize } from './stores';
@@ -8,9 +8,8 @@
 </script>
 
 <script>
-  import { createEventDispatcher, setContext } from 'svelte';
+  import { setContext } from 'svelte';
 
-  const dispatch = createEventDispatcher();
   const { Drupal } = window;
 
   export let loading = false;
@@ -43,38 +42,23 @@
       filteredRows = _rows;
     },
   });
-
-  function onSearch(event) {
-    dispatch('search', event.detail);
-  }
-
-  function onSelectCategory(event) {
-    dispatch('selectCategory', event.detail);
-  }
 </script>
 
 <!--Aligns Category filter and Grid cards side by side-->
-
-<div class="container-1">
-  <div class="box-1">
-    <slot name="left">
-      <svelte:component this={Filter} on:selectCategory={onSelectCategory} />
-    </slot>
-  </div>
-  <div class="box-2">
-    <slot name="top">
-      <div class="slot-top">
-        <svelte:component this={Search} on:search={onSearch} />
-      </div>
-    </slot>
-    <div class={`${$$props.class}`}>
+<div class="project-browser__container">
+  <aside class="project-browser__aside">
+    <slot name="left" />
+  </aside>
+  <div class="project-browser__main">
+    <slot name="top" />
+    <div class="projects-container">
       <slot name="head" />
       {#if loading}
         <Loading />
       {:else if visibleRows.length === 0}
         <div>{@html labels.empty}</div>
       {:else}
-        <ul>
+        <ul class="projects-list">
           <slot rows={visibleRows} />
         </ul>
       {/if}
@@ -86,34 +70,28 @@
 <slot name="bottom" />
 
 <style>
-  ul {
+  .projects-list {
     display: flex;
     flex-wrap: wrap;
     list-style-type: none;
     margin: 0;
   }
-  .slot-top,
-  .slot-bottom {
-    width: 100%;
-    margin-top: 1em;
+
+  .project-browser__aside {
+    flex: 1;
+  }
+  .project-browser__main {
+    flex: 4;
   }
+
   @media (min-width: 700px) {
-    .container-1 {
+    .project-browser__container {
       display: flex;
     }
   }
-  .container-1 div {
-    padding: 10px;
-  }
-  .box-1 {
-    flex: 1;
-  }
-  .box-2 {
-    flex: 4;
-  }
   /* Stack cards below category filter on smaller screens */
   @media screen and (max-width: 900px) {
-    .container-1 {
+    .project-browser__container {
       flex-direction: column;
     }
   }
diff --git a/sveltejs/src/FilterApplied.svelte b/sveltejs/src/Search/FilterApplied.svelte
similarity index 95%
rename from sveltejs/src/FilterApplied.svelte
rename to sveltejs/src/Search/FilterApplied.svelte
index 0006efb28c83b8bcca7921914b3358e773ebf7cd..5803d779342c88dddf1784f49670120e65c557fa 100644
--- a/sveltejs/src/FilterApplied.svelte
+++ b/sveltejs/src/Search/FilterApplied.svelte
@@ -1,5 +1,5 @@
 <script>
-  import { ALL_VALUES_ID, FULL_MODULE_PATH } from './constants';
+  import { ALL_VALUES_ID, FULL_MODULE_PATH } from '../constants';
 
   export let id;
   export let label;
diff --git a/sveltejs/src/FilterGroup.svelte b/sveltejs/src/Search/FilterGroup.svelte
similarity index 98%
rename from sveltejs/src/FilterGroup.svelte
rename to sveltejs/src/Search/FilterGroup.svelte
index 3e6ccf7ce8ec87e83d02712677c3ab1395dc6df0..4e04636619d5e8aae4fa28526eadfa036818b37e 100644
--- a/sveltejs/src/FilterGroup.svelte
+++ b/sveltejs/src/Search/FilterGroup.svelte
@@ -1,10 +1,10 @@
 <script>
+  import { filters } from '../stores';
+
   export let filterTitle;
   export let filterData;
   export let changeHandler;
   export let filterType;
-
-  import { filters } from './stores';
 </script>
 
 <div
diff --git a/sveltejs/src/Search.svelte b/sveltejs/src/Search/Search.svelte
similarity index 56%
rename from sveltejs/src/Search.svelte
rename to sveltejs/src/Search/Search.svelte
index e02cdb7bfc6a11dda02a57f1fcfa1a56819e2192..565f1705a5ca9084dd7b71038d33011ff5903ab6 100644
--- a/sveltejs/src/Search.svelte
+++ b/sveltejs/src/Search/Search.svelte
@@ -1,10 +1,10 @@
 <script>
   import { createEventDispatcher, getContext, onMount } from 'svelte';
-  import { slide } from 'svelte/transition';
-  import FilterGroup from './FilterGroup.svelte';
   import FilterApplied from './FilterApplied.svelte';
-  import { normalizeOptions, shallowCompare } from './util';
-  import ProjectIcon from './Project/ProjectIcon.svelte';
+  import { normalizeOptions, shallowCompare } from '../util';
+  import SearchFilters from './SearchFilters.svelte';
+  import SearchFilterToggle from './SearchFilterToggle.svelte';
+  import SearchSort from './SearchSort.svelte';
   import {
     filters,
     rowsCount,
@@ -14,7 +14,7 @@
     sort,
     searchString,
     sortCriteria,
-  } from './stores';
+  } from '../stores';
   import {
     COVERED_ID,
     ACTIVELY_MAINTAINED_ID,
@@ -24,7 +24,7 @@
     ALL_VALUES_ID,
     FULL_MODULE_PATH,
     DARK_COLOR_SCHEME,
-  } from './constants';
+  } from '../constants';
 
   const { Drupal } = window;
   const { announce } = Drupal;
@@ -45,7 +45,8 @@
     placeholder: Drupal.t('Module Name, Keyword(s), etc.'),
   };
 
-  let isOpen = false;
+  // 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;
@@ -124,24 +125,7 @@
     refreshLiveRegion();
   }
 
-  async function onSort(event) {
-    const state = stateContext.getState();
-    const detail = {
-      originalEvent: event,
-      page: state.page,
-      pageIndex: state.pageIndex,
-      pageSize: state.pageSize,
-      rows: state.filteredRows,
-      sort: $sort,
-    };
-    dispatch('sort', detail);
-    stateContext.setPage(0, 0);
-    stateContext.setRows(detail.rows);
-    sortText = $sortCriteria.find((option) => option.id === $sort).text;
-    refreshLiveRegion();
-  }
-
-  async function onAdvancedFilter(event) {
+  const onAdvancedFilter = async (event) => {
     const state = stateContext.getState();
     const detail = {
       originalEvent: event,
@@ -157,13 +141,7 @@
     stateContext.setPage(0, 0);
     stateContext.setRows(detail.rows);
     refreshLiveRegion();
-  }
-
-  /* When the user clicks on the button,
-  toggle between hiding and showing the dropdown content */
-  function openDropdown() {
-    isOpen = !isOpen;
-  }
+  };
 
   function onSelectCategory(event) {
     const state = stateContext.getState();
@@ -185,6 +163,25 @@
     $filters = $filters;
     onAdvancedFilter();
   }
+
+  /**
+   * Actions performed when clicking filter resets such as "recommended"
+   * @param {string} maintenanceId
+   *    ID of the selected maintenance status.
+   * @param {string} developmentId
+   *   ID of the selected development status.
+   * @param {string} securityId
+   *   ID of the selected security status.
+   */
+  const filterResets = (maintenanceId, developmentId, securityId) => {
+    $filters.maintenanceStatus = maintenanceId;
+    $filters.developmentStatus = developmentId;
+    $filters.securityCoverage = securityId;
+    $filters = $filters;
+    $moduleCategoryFilter = [];
+    onAdvancedFilter();
+    onSelectCategory();
+  };
 </script>
 
 <form class="search__form">
@@ -229,36 +226,15 @@
             >{Drupal.t('Sorted by @sortText', { '@sortText': sortText })}</span
           >
         </span>
-
-        {#if $filters.developmentStatus}
-          <FilterApplied
-            id={$filters.developmentStatus}
-            label={$filtersVocabularies.developmentStatus[
-              $filters.developmentStatus
-            ]}
-            clickHandler={() => removeFilter('developmentStatus')}
-          />
-        {/if}
-
-        {#if $filters.maintenanceStatus}
-          <FilterApplied
-            id={$filters.maintenanceStatus}
-            label={$filtersVocabularies.maintenanceStatus[
-              $filters.maintenanceStatus
-            ]}
-            clickHandler={() => removeFilter('maintenanceStatus')}
-          />
-        {/if}
-
-        {#if $filters.securityCoverage}
-          <FilterApplied
-            id={$filters.securityCoverage}
-            label={$filtersVocabularies.securityCoverage[
-              $filters.securityCoverage
-            ]}
-            clickHandler={() => removeFilter('securityCoverage')}
-          />
-        {/if}
+        {#each ['developmentStatus', 'maintenanceStatus', 'securityCoverage'] as filterType}
+          {#if $filters[filterType]}
+            <FilterApplied
+              id={$filters[filterType]}
+              label={$filtersVocabularies[filterType][$filters[filterType]]}
+              clickHandler={() => removeFilter(filterType)}
+            />
+          {/if}
+        {/each}
 
         {#each $moduleCategoryFilter as category}
           <FilterApplied
@@ -278,15 +254,8 @@
         {#if $filters.securityCoverage !== ALL_VALUES_ID || $filters.maintenanceStatus !== ALL_VALUES_ID || $filters.developmentStatus !== ALL_VALUES_ID || $moduleCategoryFilter.length}
           <button
             class="search__filter-button"
-            on:click={() => {
-              $filters.maintenanceStatus = ALL_VALUES_ID;
-              $filters.developmentStatus = ALL_VALUES_ID;
-              $filters.securityCoverage = ALL_VALUES_ID;
-              $filters = $filters;
-              $moduleCategoryFilter = [];
-              onAdvancedFilter();
-              onSelectCategory();
-            }}
+            on:click|preventDefault={() =>
+              filterResets(ALL_VALUES_ID, ALL_VALUES_ID, ALL_VALUES_ID)}
           >
             {Drupal.t('Clear filters')}
           </button>
@@ -294,115 +263,19 @@
         {#if !($filters.maintenanceStatus === ACTIVELY_MAINTAINED_ID && $filters.securityCoverage === COVERED_ID && $filters.developmentStatus === ALL_VALUES_ID && $moduleCategoryFilter.length === 0)}
           <button
             class="search__filter-button"
-            on:click={() => {
-              $filters.maintenanceStatus = ACTIVELY_MAINTAINED_ID;
-              $filters.securityCoverage = COVERED_ID;
-              $filters.developmentStatus = ALL_VALUES_ID;
-              $moduleCategoryFilter = [];
-              $filters = $filters;
-              onAdvancedFilter();
-              onSelectCategory();
-            }}
+            on:click|preventDefault={() =>
+              filterResets(ACTIVELY_MAINTAINED_ID, ALL_VALUES_ID, COVERED_ID)}
           >
             {Drupal.t('Recommended filters')}
           </button>
         {/if}
       </div>
     </section>
-    <div class="search__sort">
-      <label for="pb-sort">{Drupal.t('Sort by:')}</label>
-      <select
-        name="pb-sort"
-        id="pb-sort"
-        bind:value={$sort}
-        on:change={onSort}
-        class="search__sort-select form-select form-element form-element--type-select"
-      >
-        {#each $sortCriteria as opt}
-          <option value={opt.id}>
-            {opt.text}
-          </option>
-        {/each}
-      </select>
-    </div>
-    <div class="search__filter">
-      <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"
-          />Filters
-        </button>
-      </section>
-    </div>
+    <SearchSort on:sort bind:sortText refresh={refreshLiveRegion} />
+    <SearchFilterToggle bind:isOpen={filtersOpen} />
   </div>
   <div class="search__dropdown dropdown-filters" id="filter-dropdown">
-    {#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">
-                <ProjectIcon type="status" />
-              </span>
-            {/if}
-          </label>
-        </FilterGroup>
-      </div>
-    {/if}
+    <SearchFilters bind:isOpen={filtersOpen} {onAdvancedFilter} />
   </div>
 </form>
 
@@ -445,7 +318,7 @@
     inset-inline-end: 30px;
   }
 
-  ::placeholder {
+  .search__searchterm::placeholder {
     font-family: sans-serif;
     font-style: normal;
     font-weight: 400;
@@ -455,39 +328,12 @@
     align-items: center;
   }
 
-  .search__filter__toggle > img {
-    width: 20px;
-    height: 14px;
-    margin-bottom: -2px;
-    margin-inline-end: 4px;
-  }
-
-  .search__filter__toggle.is_open {
-    background-color: #adaeb3;
-  }
-
   .dropdown-filters {
     position: relative;
     border: 3px solid #f3f4f9;
     z-index: 1;
   }
 
-  .small-icons {
-    margin-top: -3px;
-    margin-inline-end: 1px;
-  }
-
-  .search__checkbox-label {
-    font-weight: normal;
-    padding-inline-start: 35px;
-    display: flex;
-  }
-
-  .form-select:hover,
-  .search__filter__toggle:hover {
-    cursor: pointer;
-  }
-
   #output {
     display: inline-block;
     font-family: sans-serif;
@@ -509,31 +355,6 @@
     max-width: 100%;
   }
 
-  .search__sort-select {
-    border: none;
-    background-color: #d3d4d9;
-  }
-
-  .search__sort {
-    z-index: 2;
-  }
-
-  .search__filter__toggle {
-    background-color: #d3d4d9;
-    color: black;
-    padding-left: 16px;
-    padding-right: 16px;
-    font-size: 16px;
-    border-radius: 2px;
-    width: fit-content;
-    margin-inline-start: 15px;
-    text-decoration: underline;
-  }
-
-  .search__filter__toggle > img {
-    padding: 0 0.25rem;
-  }
-
   .search__filter-button {
     padding: 0 0.25rem;
     background: none;
@@ -543,41 +364,9 @@
     cursor: pointer;
   }
 
-  .search__filter {
-    display: flex;
-    margin-inline-end: 1em;
-  }
-
-  @media only screen and (min-width: 1200px) {
-    .search__checkbox-label {
-      font-weight: normal;
-      padding-inline-start: 35px;
-      display: flex;
-      margin-right: unset;
-    }
-  }
-
   @media screen and (max-width: 855px) {
     .search__grid-container {
       display: block;
     }
-    .search__sort {
-      margin-bottom: 10px;
-      margin-top: 10px;
-    }
-  }
-
-  @media (forced-colors: active) {
-    .search__filter__toggle {
-      border: 1px solid;
-    }
-    #pb-sort {
-      border: 1px solid;
-    }
-    @media (prefers-color-scheme: dark) {
-      .search__filter__toggle > img {
-        filter: invert(1);
-      }
-    }
   }
 </style>
diff --git a/sveltejs/src/Search/SearchFilterToggle.svelte b/sveltejs/src/Search/SearchFilterToggle.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..4dd3e291690d26a4286274693375cce18229ce24
--- /dev/null
+++ b/sveltejs/src/Search/SearchFilterToggle.svelte
@@ -0,0 +1,76 @@
+<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>
+
+<style>
+  .search__filter__toggle-container {
+    display: flex;
+    margin-inline-end: 1em;
+  }
+
+  .search__filter__toggle {
+    background-color: #d3d4d9;
+    color: black;
+    padding-left: 16px;
+    padding-right: 16px;
+    font-size: 16px;
+    border-radius: 2px;
+    width: fit-content;
+    margin-inline-start: 15px;
+    text-decoration: underline;
+  }
+  .search__filter__toggle.is_open {
+    background-color: #adaeb3;
+  }
+  .search__filter__toggle:hover {
+    cursor: pointer;
+  }
+
+  .search__filter__toggle-img {
+    width: 20px;
+    height: 14px;
+    padding: 0 0.25rem;
+    margin-bottom: -2px;
+    margin-inline-end: 4px;
+  }
+  @media (forced-colors: active) {
+    .search__filter__toggle {
+      border: 1px solid;
+    }
+    @media (prefers-color-scheme: dark) {
+      .search__filter__toggle-img {
+        filter: invert(1);
+      }
+    }
+  }
+</style>
diff --git a/sveltejs/src/Search/SearchFilters.svelte b/sveltejs/src/Search/SearchFilters.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e41232cc91517fd6d2e264149fd5d82ab1f8a9bf
--- /dev/null
+++ b/sveltejs/src/Search/SearchFilters.svelte
@@ -0,0 +1,94 @@
+<script>
+  import { slide } from 'svelte/transition';
+  import FilterGroup from './FilterGroup.svelte';
+  import ProjectIcon from '../Project/ProjectIcon.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">
+            <ProjectIcon type="status" />
+          </span>
+        {/if}
+      </label>
+    </FilterGroup>
+  </div>
+{/if}
+
+<style>
+  @media only screen and (min-width: 1200px) {
+    .small-icons {
+      margin-top: -3px;
+      margin-inline-end: 1px;
+    }
+    .search__checkbox-label {
+      font-weight: normal;
+      padding-inline-start: 35px;
+      display: flex;
+    }
+    .search__checkbox-label {
+      font-weight: normal;
+      padding-inline-start: 35px;
+      display: flex;
+      margin-right: unset;
+    }
+  }
+</style>
diff --git a/sveltejs/src/Search/SearchSort.svelte b/sveltejs/src/Search/SearchSort.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2c66f18e0a5f2d4e22354dd64c2153ce5ce60d5a
--- /dev/null
+++ b/sveltejs/src/Search/SearchSort.svelte
@@ -0,0 +1,72 @@
+<script>
+  import { createEventDispatcher, getContext } from 'svelte';
+  import { sort, sortCriteria } from '../stores';
+
+  export let sortText;
+  export let refresh;
+
+  const { Drupal } = window;
+  const dispatch = createEventDispatcher();
+  const stateContext = getContext('state');
+
+  async function onSort(event) {
+    const state = stateContext.getState();
+    const detail = {
+      originalEvent: event,
+      page: state.page,
+      pageIndex: state.pageIndex,
+      pageSize: state.pageSize,
+      rows: state.filteredRows,
+      sort: $sort,
+    };
+
+    dispatch('sort', detail);
+    stateContext.setPage(0, 0);
+    stateContext.setRows(detail.rows);
+    sortText = $sortCriteria.find((option) => option.id === $sort).text;
+    refresh();
+  }
+</script>
+
+<div class="search__sort">
+  <label for="pb-sort">{Drupal.t('Sort by:')}</label>
+  <select
+    name="pb-sort"
+    id="pb-sort"
+    bind:value={$sort}
+    on:change={onSort}
+    class="search__sort-select form-select form-element form-element--type-select"
+  >
+    {#each $sortCriteria as opt}
+      <option value={opt.id}>
+        {opt.text}
+      </option>
+    {/each}
+  </select>
+</div>
+
+<style>
+  .search__sort {
+    z-index: 2;
+  }
+
+  .search__sort-select {
+    border: none;
+    background-color: #d3d4d9;
+  }
+  .search__sort-select:hover {
+    cursor: pointer;
+  }
+
+  @media screen and (max-width: 855px) {
+    .search__sort {
+      margin-bottom: 10px;
+      margin-top: 10px;
+    }
+  }
+  @media (forced-colors: active) {
+    #pb-sort {
+      border: 1px solid;
+    }
+  }
+</style>
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php
index 94bfd583bb13999e0ef4f03f31a8c0706e384d14..fce95669a9aaff9b2cc3c94cef1a55620355d17d 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php
@@ -60,7 +60,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
     $assert_session = $this->assertSession();
     $this->drupalGet('admin/modules/browse');
     $this->svelteInitHelper('text', 'Cream cheese on a bagel');
-    $cream_cheese_module_selector = '#project-browser .box-2 ul > li:nth-child(1)';
+    $cream_cheese_module_selector = '#project-browser .project-browser__main ul > li:nth-child(1)';
     $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button");
     $this->assertNotEmpty($download_button);
     $this->assertSame('Add and Install Cream cheese on a bagel', $download_button->getText());
@@ -83,7 +83,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
 
     $this->drupalGet('admin/modules/browse');
     $this->svelteInitHelper('text', 'Pinky and the Brain');
-    $pinky_brain_selector = '#project-browser .box-2 ul > li:nth-child(2)';
+    $pinky_brain_selector = '#project-browser .project-browser__main ul > li:nth-child(2)';
     $action_button = $assert_session->waitForElementVisible('css', "$pinky_brain_selector button");
     $this->assertNotEmpty($action_button);
     $this->assertSame('Install Pinky and the Brain', $action_button->getText());
@@ -114,7 +114,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
     $this->drupalGet('admin/modules/browse');
     $this->svelteInitHelper('text', 'Pinky and the Brain');
 
-    $cream_cheese_module_selector = '#project-browser .box-2 ul > li:nth-child(1)';
+    $cream_cheese_module_selector = '#project-browser .project-browser__main ul > li:nth-child(1)';
     $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button");
     $this->assertNotEmpty($download_button);
     $this->assertSame('Add and Install Cream cheese on a bagel', $download_button->getText());
@@ -147,7 +147,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
     $this->svelteInitHelper('text', 'Cream cheese on a bagel');
     // Try beginning another install while one is in progress, but not yet in
     // the applying stage.
-    $cream_cheese_module_selector = '#project-browser .box-2 ul > li:nth-child(1)';
+    $cream_cheese_module_selector = '#project-browser .project-browser__main ul > li:nth-child(1)';
     $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button");
     $cream_cheese_button->click();
 
@@ -182,7 +182,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
     $this->svelteInitHelper('text', 'Cream cheese on a bagel');
     // Try beginning another install while one is in progress, but not yet in
     // the applying stage.
-    $cream_cheese_module_selector = '#project-browser .box-2 ul > li:nth-child(1)';
+    $cream_cheese_module_selector = '#project-browser .project-browser__main ul > li:nth-child(1)';
     $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button");
     $cream_cheese_button->click();
     $this->assertTrue($assert_session->waitForText('The install staging area was locked less than 1 minutes ago. This is recent enough that a legitimate installation may be in progress. Consider waiting before unlocking the installation staging area.'));
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
index 87bdb039701fe4fe05d94f3e591e8d95b916a4d0..cafb2c6b63921b90fbe4b687e5b88e7f21bd7c05 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
@@ -227,10 +227,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->svelteInitHelper('text', 'Helvetica');
     $page->clickLink('Helvetica');
     $this->assertTrue($assert_session->waitForText('Categories:'));
-    $link = $page->find('css', '.module-page__wrapper__box-2--project-description a');
+    $link = $page->find('css', '.module-page__project-description a');
     $target = $link->getAttribute('target');
     $this->assertEquals('_blank', $target);
-
   }
 
   /**
@@ -373,7 +372,6 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->svelteInitHelper('text', 'Astronaut Simulator');
     $this->pressWithWait('Clear filters');
     $this->pressWithWait('Recommended filters');
-
     $this->assertProjectsVisible([
       'Cream cheese on a bagel',
       'Pinky and the Brain',
@@ -797,7 +795,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $result_count_text = $page->find('css', '.search__results-count #output')->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.container-1 > div.box-1 > div > form > section > details > fieldset > label:nth-child(3)';
+    $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.
@@ -940,13 +938,13 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
       $this->svelteInitHelper('css', $selector['selector']);
       $this->getSession()->getPage()->pressButton($selector['value']);
       $this->svelteInitHelper('text', 'Helvetica');
-      $assert_session->elementsCount('css', '#project-browser .box-2 ul li:nth-child(7) .categories ul li', 1);
-      $grid_text = $this->getElementText('#project-browser .box-2 ul li:nth-child(7) .categories ul li:nth-child(1)');
+      $assert_session->elementsCount('css', '#project-browser .project-browser__main ul li:nth-child(7) .categories ul li', 1);
+      $grid_text = $this->getElementText('#project-browser .project-browser__main ul li:nth-child(7) .categories ul li:nth-child(1)');
       $this->assertEquals('E-commerce', $grid_text);
-      $assert_session->elementsCount('css', '#project-browser .box-2  ul li:nth-child(9) .categories ul li', 2);
-      $grid_text = $this->getElementText('#project-browser .box-2 ul li:nth-child(9) .categories ul li:nth-child(1)');
+      $assert_session->elementsCount('css', '#project-browser .project-browser__main  ul li:nth-child(9) .categories ul li', 2);
+      $grid_text = $this->getElementText('#project-browser .project-browser__main ul li:nth-child(9) .categories ul li:nth-child(1)');
       $this->assertEquals('Commerce/Advertising', $grid_text);
-      $grid_text = $this->getElementText('#project-browser .box-2 ul li:nth-child(9) .categories ul li:nth-child(2)');
+      $grid_text = $this->getElementText('#project-browser .project-browser__main ul li:nth-child(9) .categories ul li:nth-child(2)');
       $this->assertEquals('E-commerce', $grid_text);
     }
   }
@@ -1001,7 +999,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->inputSearchField('inline form errors');
     $this->svelteInitHelper('text', 'Inline Form Errors');
 
-    $install_link = $page->find('css', '.box-2 .action-button__wrapper a');
+    $install_link = $page->find('css', '.project-browser__main .action-button__wrapper a');
 
     $this->assertStringContainsString('admin/modules#module-inline-form-errors', $install_link->getAttribute('href'));
     $this->drupalGet($install_link->getAttribute('href'));