diff --git a/local/languages/de.ini b/local/languages/de.ini
index fc2852d71200a869d5c844346094cca46eb94636..6428ea9568274ecf180264ac9f35778dece09cc1 100644
--- a/local/languages/de.ini
+++ b/local/languages/de.ini
@@ -2054,7 +2054,6 @@ DE-Kn38 = "Hochschule für Musik und Tanz Köln"
 Range-from-to = "Bereich von/bis"
 
 ; #17833
-form-legend = "Bitte füllen Sie alle Felder aus"
 form-button-submit = "Ausgefülltes Formular abschicken"
 
 ; #17601
@@ -2079,4 +2078,6 @@ missing_record_exception = "Der aufgerufene Titel (%%id%%) ist nicht vorhanden."
 Unknown Electronic = "Titel ist beim Resolver-Service nicht bekannt"
 
 ; #20826
-title_wrapper = "%%pageTitle%% %%titleSeparator%% %%siteTitle%%"
\ No newline at end of file
+title_wrapper = "%%pageTitle%% %%titleSeparator%% %%siteTitle%%"
+
+load_tab_content_hint = "Klicken Sie hier, um den Inhalt der Registerkarte zu laden."
diff --git a/local/languages/en.ini b/local/languages/en.ini
index e3c2d98bdcae9c74408219a851055e1d2ed5d0cb..f1aa7b7cf478120dce8ec464464eec64899cca2b 100644
--- a/local/languages/en.ini
+++ b/local/languages/en.ini
@@ -1949,7 +1949,6 @@ resolver_link_access_unknown = "Record unknown to resolver"
 ; message to be shown upon empty resolver response
 no_resolver_links = "No online links available."
 
-
 ; reset password
 reset_password_text = "Please complete the form below to reset your password. You will receive an email after we have completed resetting your password."
 Reset Password = "Reset Password"
@@ -2136,7 +2135,6 @@ DE-Kn38 = "Hochschule für Musik und Tanz Köln"
 Range-from-to = "Range from/to"
 
 ; #17833
-form-legend = "Please fill in all fields to create an account"
 form-button-submit = "Submit the completed form"
 
 ; #17601
@@ -2167,4 +2165,6 @@ missing_record_exception = "Record %%id%% is unavailable."
 Unknown Electronic = "Record is unknown to the resolver service"
 
 ; #20826
-title_wrapper = "%%pageTitle%% %%titleSeparator%% %%siteTitle%%"
\ No newline at end of file
+title_wrapper = "%%pageTitle%% %%titleSeparator%% %%siteTitle%%"
+
+load_tab_content_hint = "Click to load tab content."
diff --git a/themes/finc-accessibility/js/record.js b/themes/finc-accessibility/js/record.js
new file mode 100644
index 0000000000000000000000000000000000000000..9766867212c7524e3ed39a0a6eac7c250fc27d76
--- /dev/null
+++ b/themes/finc-accessibility/js/record.js
@@ -0,0 +1,323 @@
+/*global deparam, getUrlRoot, grecaptcha, recaptchaOnLoad, resetCaptcha, syn_get_widget, userIsLoggedIn, VuFind, setupJumpMenus */
+/*exported ajaxTagUpdate, recordDocReady, refreshTagListCallback */
+
+/**
+ * Functions and event handlers specific to record pages.
+ */
+function checkRequestIsValid(element, requestType) {
+  var recordId = element.href.match(/\/Record\/([^/]+)\//)[1];
+  var vars = deparam(element.href);
+  vars.id = recordId;
+
+  var url = VuFind.path + '/AJAX/JSON?' + $.param({
+    method: 'checkRequestIsValid',
+    id: recordId,
+    requestType: requestType,
+    data: vars
+  });
+  $.ajax({
+    dataType: 'json',
+    cache: false,
+    url: url
+  })
+    .done(function checkValidDone(response) {
+      if (response.data.status) {
+        $(element).removeClass('disabled')
+          .attr('title', response.data.msg)
+          .html('<i class="fa fa-flag" aria-hidden="true"></i>&nbsp;' + response.data.msg);
+      } else {
+        $(element).remove();
+      }
+    })
+    .fail(function checkValidFail(/*response*/) {
+      $(element).remove();
+    });
+}
+
+function setUpCheckRequest() {
+  $('.checkRequest').each(function checkRequest() {
+    checkRequestIsValid(this, 'Hold');
+  });
+  $('.checkStorageRetrievalRequest').each(function checkStorageRetrievalRequest() {
+    checkRequestIsValid(this, 'StorageRetrievalRequest');
+  });
+  $('.checkILLRequest').each(function checkILLRequest() {
+    checkRequestIsValid(this, 'ILLRequest');
+  });
+}
+
+function deleteRecordComment(element, recordId, recordSource, commentId) {
+  var url = VuFind.path + '/AJAX/JSON?' + $.param({ method: 'deleteRecordComment', id: commentId });
+  $.ajax({
+    dataType: 'json',
+    url: url
+  })
+    .done(function deleteCommentDone(/*response*/) {
+      $($(element).closest('.comment')[0]).remove();
+    });
+}
+
+function refreshCommentList($target, recordId, recordSource) {
+  var url = VuFind.path + '/AJAX/JSON?' + $.param({
+    method: 'getRecordCommentsAsHTML',
+    id: recordId,
+    source: recordSource
+  });
+  $.ajax({
+    dataType: 'json',
+    url: url
+  })
+    .done(function refreshCommentListDone(response) {
+      // Update HTML
+      var $commentList = $target.find('.comment-list');
+      $commentList.empty();
+      $commentList.append(response.data.html);
+      $commentList.find('.delete').unbind('click').click(function commentRefreshDeleteClick() {
+        var commentId = $(this).attr('id').substr('recordComment'.length);
+        deleteRecordComment(this, recordId, recordSource, commentId);
+        return false;
+      });
+      $target.find('.comment-form input[type="submit"]').button('reset');
+      resetCaptcha($target);
+    });
+}
+
+function registerAjaxCommentRecord(_context) {
+  var context = typeof _context === "undefined" ? document : _context;
+  // Form submission
+  $(context).find('form.comment-form').unbind('submit').submit(function commentFormSubmit() {
+    var form = this;
+    var id = form.id.value;
+    var recordSource = form.source.value;
+    var url = VuFind.path + '/AJAX/JSON?' + $.param({ method: 'commentRecord' });
+    var data = {
+      comment: form.comment.value,
+      id: id,
+      source: recordSource
+    };
+    if (typeof grecaptcha !== 'undefined') {
+      var recaptcha = $(form).find('.g-recaptcha');
+      if (recaptcha.length > 0) {
+        data['g-recaptcha-response'] = grecaptcha.getResponse(recaptcha.data('captchaId'));
+      }
+    }
+    $.ajax({
+      type: 'POST',
+      url: url,
+      data: data,
+      dataType: 'json'
+    })
+      .done(function addCommentDone(/*response, textStatus*/) {
+        var $form = $(form);
+        var $tab = $form.closest('.list-tab-content');
+        if (!$tab.length) {
+          $tab = $form.closest('.tab-pane');
+        }
+        refreshCommentList($tab, id, recordSource);
+        $form.find('textarea[name="comment"]').val('');
+        $form.find('input[type="submit"]').button('loading');
+        resetCaptcha($form);
+      })
+      .fail(function addCommentFail(response, textStatus) {
+        if (textStatus === 'abort' || typeof response.responseJSON === 'undefined') { return; }
+        VuFind.lightbox.alert(response.responseJSON.data, 'danger');
+      });
+    return false;
+  });
+  // Delete links
+  $('.delete').click(function commentDeleteClick() {
+    var commentId = this.id.substr('recordComment'.length);
+    deleteRecordComment(this, $('.hiddenId').val(), $('.hiddenSource').val(), commentId);
+    return false;
+  });
+  // Prevent form submit
+  return false;
+}
+
+function registerTabEvents() {
+  // Logged in AJAX
+  registerAjaxCommentRecord();
+  // Render recaptcha
+  recaptchaOnLoad();
+
+  setUpCheckRequest();
+
+  VuFind.lightbox.bind('.tab-pane.active');
+}
+
+function removeHashFromLocation() {
+  if (window.history.replaceState) {
+    var href = window.location.href.split('#');
+    window.history.replaceState({}, document.title, href[0]);
+  } else {
+    window.location.hash = '#';
+  }
+}
+
+function ajaxLoadTab($newTab, tabid, setHash) {
+  // Request the tab via AJAX:
+  $.ajax({
+    url: VuFind.path + getUrlRoot(document.URL) + '/AjaxTab',
+    type: 'POST',
+    data: {tab: tabid}
+  })
+    .always(function ajaxLoadTabDone(data) {
+      if (typeof data === 'object') {
+        $newTab.html(data.responseText ? data.responseText : VuFind.translate('error_occurred'));
+      } else {
+        $newTab.html(data);
+      }
+      registerTabEvents();
+      if (typeof syn_get_widget === "function") {
+        syn_get_widget();
+      }
+      if (typeof setHash == 'undefined' || setHash) {
+        window.location.hash = tabid;
+      } else {
+        removeHashFromLocation();
+      }
+      setupJumpMenus($newTab);
+    });
+  return false;
+}
+
+function refreshTagList(_target, _loggedin) {
+  var loggedin = !!_loggedin || userIsLoggedIn;
+  var target = _target || document;
+  var recordId = $(target).find('.hiddenId').val();
+  var recordSource = $(target).find('.hiddenSource').val();
+  var $tagList = $(target).find('.tagList');
+  if ($tagList.length > 0) {
+    var url = VuFind.path + '/AJAX/JSON?' + $.param({
+      method: 'getRecordTags',
+      id: recordId,
+      source: recordSource
+    });
+    $.ajax({
+      dataType: 'json',
+      url: url
+    })
+      .done(function getRecordTagsDone(response) {
+        $tagList.empty();
+        $tagList.replaceWith(response.data.html);
+        if (loggedin) {
+          $tagList.addClass('loggedin');
+        } else {
+          $tagList.removeClass('loggedin');
+        }
+      });
+  }
+}
+function refreshTagListCallback() {
+  refreshTagList(false, true);
+}
+
+function ajaxTagUpdate(_link, tag, _remove) {
+  var link = _link || document;
+  var remove = _remove || false;
+  var $target = $(link).closest('.record');
+  var recordId = $target.find('.hiddenId').val();
+  var recordSource = $target.find('.hiddenSource').val();
+  $.ajax({
+    url: VuFind.path + '/AJAX/JSON?method=tagRecord',
+    method: 'POST',
+    data: {
+      tag: '"' + tag.replace(/\+/g, ' ') + '"',
+      id: recordId,
+      source: recordSource,
+      remove: remove
+    }
+  })
+    .always(function tagRecordAlways() {
+      refreshTagList($target, false);
+    });
+}
+
+function getNewRecordTab(tabid) {
+  return $('<div class="tab-pane ' + tabid + '-tab" id="' + tabid + '" role="tabpanel" tabindex="-1" aria-labelledby="' + tabid + '-tabselector"><i class="fa fa-spinner fa-spin" aria-hidden="true"></i> ' + VuFind.translate('loading') + '...</div>');
+}
+
+function backgroundLoadTab(tabid) {
+  if ($('.' + tabid + '-tab').length > 0) {
+    return;
+  }
+  var newTab = getNewRecordTab(tabid);
+  $('.nav-tabs a.' + tabid).closest('.result,.record').find('.tab-content').append(newTab);
+  return ajaxLoadTab(newTab, tabid, false);
+}
+
+function applyRecordTabHash() {
+  var activeTab = $('.record-tabs li.active').attr('data-tab');
+  var $initiallyActiveTab = $('.record-tabs li.initiallyActive a');
+  var newTab = typeof window.location.hash !== 'undefined'
+    ? window.location.hash.toLowerCase() : '';
+
+  // Open tab in url hash
+  if (newTab.length <= 1 || newTab === '#tabnav') {
+    $initiallyActiveTab.click();
+  } else if (newTab.length > 1 && '#' + activeTab !== newTab) {
+    $('.' + newTab.substr(1) + ' a').click();
+  }
+}
+
+$(window).on('hashchange', applyRecordTabHash);
+
+function recordDocReady() {
+  $('.record-tabs .nav-tabs a').click(function recordTabsClick() {
+    var $li = $(this).parent();
+    // If it's an active tab, click again to follow to a shareable link.
+    if ($li.hasClass('active')) {
+      return true;
+    }
+    var tabid = $li.attr('data-tab');
+    var $top = $(this).closest('.record-tabs');
+
+    // accessibility: mark tab controls as selected
+    $top.find('.record-tab.active').find('a').attr('aria-selected', 'false');
+    $('#' + tabid + '-tabselector').attr('aria-selected', 'true').attr('aria-controls', tabid);
+
+    // accessibility: set aria-hidden for content panes
+    $top.find('.tab-pane.active').removeClass('active').attr('aria-hidden', 'true');
+    $top.find('.' + tabid + '-tab').addClass('active').attr('aria-hidden', 'false');
+
+    // if we're flagged to skip AJAX for this tab, we need special behavior:
+    if ($li.hasClass('noajax')) {
+      // if this was the initially active tab, we have moved away from it and
+      // now need to return -- just switch it back on.
+      if ($li.hasClass('initiallyActive')) {
+        $(this).tab('show');
+        window.location.hash = 'tabnav';
+        return false;
+      }
+      // otherwise, we need to let the browser follow the link:
+      return true;
+    }
+    $(this).tab('show');
+    if ($top.find('.' + tabid + '-tab').length > 0) {
+      $top.find('.' + tabid + '-tab').addClass('active');
+      if ($top.find('#' + tabid ).length) {
+        $top.find('#' + tabid ).parent().focus();
+      }
+      if ($(this).parent().hasClass('initiallyActive')) {
+        removeHashFromLocation();
+      } else {
+        window.location.hash = tabid;
+      }
+      return false;
+    } else {
+      var newTab = getNewRecordTab(tabid).addClass('active');
+      $top.find('.tab-content').append(newTab);
+      if ($top.find('#' + tabid ).length) {
+        $top.find('#' + tabid ).parent().focus();
+      }
+      return ajaxLoadTab(newTab, tabid, !$(this).parent().hasClass('initiallyActive'));
+    }
+  });
+
+  $('[data-background]').each(function setupBackgroundTabs(index, el) {
+    backgroundLoadTab(el.className);
+  });
+
+  registerTabEvents();
+  applyRecordTabHash();
+}
diff --git a/themes/finc-accessibility/js/vendor/bootstrap-accessibility-en.min.js b/themes/finc-accessibility/js/vendor/bootstrap-accessibility-en.min.js
index 630f112aed7e6a0149687a95215c623908bf0404..fb3ad97f9a4462ee6e30273db5a316df5ee84feb 100644
--- a/themes/finc-accessibility/js/vendor/bootstrap-accessibility-en.min.js
+++ b/themes/finc-accessibility/js/vendor/bootstrap-accessibility-en.min.js
@@ -3,7 +3,6 @@
 * Copyright (c) 2020 PayPal Accessibility Team; Licensed BSD */
 !function ($) {
     "use strict";
-    console.log('en');
     var uniqueId = function (prefix) {
         return (prefix || "ui-id") + "-" + Math.floor(1e3 * Math.random() + 1)
     }, focusable = function (element, isTabIndexNotNaN) {
diff --git a/themes/finc-accessibility/scss/compiled.scss b/themes/finc-accessibility/scss/compiled.scss
index 766002b57789f053a07aea5a5099e79b2f515732..db481aca78be66dffc9e30ba18c9a77e01996dbd 100644
--- a/themes/finc-accessibility/scss/compiled.scss
+++ b/themes/finc-accessibility/scss/compiled.scss
@@ -54,4 +54,10 @@
 a.remove-filter {
   display: flex;
   width: 100%;
+}
+
+.record-tab.active{
+  .load-tab-content {
+    display: none;
+  }
 }
\ No newline at end of file
diff --git a/themes/finc-accessibility/templates/Recommend/SideFacets/cluster-list.phtml b/themes/finc-accessibility/templates/Recommend/SideFacets/cluster-list.phtml
index 293af05732827251527ee22b281cc69fe25b3cae..1b1f8adcbba51d11ecdae3e1785993b1e8cfff98 100644
--- a/themes/finc-accessibility/templates/Recommend/SideFacets/cluster-list.phtml
+++ b/themes/finc-accessibility/templates/Recommend/SideFacets/cluster-list.phtml
@@ -30,7 +30,7 @@
   ]) ?>
 <?php endforeach; ?>
 <?php if (empty($this->cluster['list'])): ?>
-  <div class="facet"><?=$this->transEsc('facet_list_empty')?></div>
+  <span class="facet"><?=$this->transEsc('facet_list_empty')?></span>
 <?php endif; ?>
 
 <?php /* LESS and SEE MORE links */ ?>
diff --git a/themes/finc-accessibility/templates/Recommend/SideFacets/filter-list.phtml b/themes/finc-accessibility/templates/Recommend/SideFacets/filter-list.phtml
index 3957f6ba12e6ac722e65ea77266a15df592e7e73..3d2c763fce3e185c1e9f000fd9d06f4856a12c75 100644
--- a/themes/finc-accessibility/templates/Recommend/SideFacets/filter-list.phtml
+++ b/themes/finc-accessibility/templates/Recommend/SideFacets/filter-list.phtml
@@ -1,7 +1,7 @@
 <!-- finc-accessibility - Recommend - SideFacets - filter-list.phtml -->
 <?php /* #18509 copied from bootstrap for adding language tags to displayText */ ?>
 <div class="facet-group active-filters">
-  <div class="title"><?=$this->transEsc('Remove Filters')?> <span class="sr-only"><?=$this->transEsc('facet_deselect_hint') ?></div>
+  <div class="title"><?=$this->transEsc('Remove Filters')?> <span class="sr-only"><?=$this->transEsc('facet_deselect_hint') ?></span></div>
   <ul>
   <?php foreach ($filterList as $field => $filters): ?>
     <?php foreach ($filters as $i => $filter): ?>
diff --git a/themes/finc/js/lightbox.js b/themes/finc/js/lightbox.js
index 4d183e226b88d770ccea6bb32f1779abf431f7cd..ae7297e5b0f6290395b5d785dbaba82add5a3a94 100644
--- a/themes/finc/js/lightbox.js
+++ b/themes/finc/js/lightbox.js
@@ -114,6 +114,16 @@ VuFind.register('lightbox', function Lightbox() {
     $('#modal').find('.checkbox-select-item').change(function lbSelectAllDisable() {
       $(this).closest('.modal-body').find('.checkbox-select-all').prop('checked', false);
     });
+
+    // #19695 accessibility: set id for aria-label
+    if (_modalBody.find('h1').length) {
+      _modalBody.find('h1:first').attr('id', 'modal-title');
+      $('#modal').attr('aria-labelledby', 'modal-title');
+    } else if (_modalBody.find('h2').length) {
+      _modalBody.find('h2:first').attr('id', 'modal-title');
+      $('#modal').attr('aria-labelledby', 'modal-title');
+    }
+
     // Recaptcha
     recaptchaOnLoad();
   }
diff --git a/themes/finc/templates/Recommend/SideFacets.phtml b/themes/finc/templates/Recommend/SideFacets.phtml
index 8c7fff5acfa902339f2eaf28c3f5376178d00a96..3e524b476825eedf817ba8fa2c8b110c3aadf186 100644
--- a/themes/finc/templates/Recommend/SideFacets.phtml
+++ b/themes/finc/templates/Recommend/SideFacets.phtml
@@ -55,9 +55,9 @@ if ($hierarchicalFacets) {
 <?php if (!empty($sideFacetSet) && $results->getResultTotal() > 0): ?>
   <?php foreach ($sideFacetSet as $title => $cluster): ?>
     <div class="facet-group" id="side-panel-<?=$this->escapeHtmlAttr($title)?>">
-      <button <?php if(in_array($title, $collapsedFacets)): ?>class="title collapsed" aria-expanded="false"<?php else: ?>class="title" aria-expanded="true"<?php endif ?> data-toggle="collapse" href="#side-collapse-<?=$this->escapeHtmlAttr($title) ?>" >
+      <a <?php if(in_array($title, $collapsedFacets)): ?>class="title collapsed" aria-expanded="false"<?php else: ?>class="title" aria-expanded="true"<?php endif ?> data-toggle="collapse" href="#side-collapse-<?=$this->escapeHtmlAttr($title) ?>" >
         <?=$this->transEsc($cluster['label'])?> <span class="sr-only"><?=$this->transEsc('facet_select_hint') ?></span>
-      </button>
+      </a>
       <ul id="side-collapse-<?=$this->escapeHtmlAttr($title)?>" class="collapse<?php if (!in_array($title, $collapsedFacets)): ?> in<?php endif ?>">
         <?=$this->context($this)->renderInContext(
           'Recommend/SideFacets/facet.phtml',
diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/link-isn.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/link-isn.phtml
index 77bed61f58f3b6a7a2cfc3113ec6a2fbb194be64..30507b95f9bba7a18fa8cdd5d980856dfd166994 100644
--- a/themes/finc/templates/RecordDriver/DefaultRecord/link-isn.phtml
+++ b/themes/finc/templates/RecordDriver/DefaultRecord/link-isn.phtml
@@ -1,9 +1,9 @@
 <?php
 /* use advanced search if we have multiple issns */
 if (is_array($this->lookfor) && count($this->lookfor) > 1) {
-  $query = '?join=AND&amp;bool0[]=OR';
+  $query = '?join=AND&amp;bool0%5B%5D=OR';
   foreach ($this->lookfor as $issn) {
-    $query .= '&amp;lookfor0[]=' . urlencode($issn) . '&amp;type0[]=ISN';
+    $query .= '&amp;lookfor0%5B%5D=' . urlencode($issn) . '&amp;type0%5B%5D=ISN';
   }
 } elseif (count($this->lookfor) == 1) {
   $query = '?lookfor=%22' . urlencode($this->lookfor[0]) . '%22&amp;type=ISN';
diff --git a/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml b/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml
index e21b80019c88987fa4b300e1455720c988b77584..63c4ed5c15e04155f7917b395ef565e2dd4f658b 100644
--- a/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml
+++ b/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml
@@ -4,6 +4,7 @@ $coverDetails = $this->record($this->driver)->getCoverDetails('result-list', 'me
 $cover = $coverDetails['html'];
 $thumbnail = false;
 $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('result');
+$describedById = $driver->getSourceIdentifier() . '|' . $driver->getUniqueId();
 if ($cover):
   ob_start(); ?>
   <div class="media-<?=$thumbnailAlignment?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>" aria-hidden="true">
@@ -31,7 +32,7 @@ if ($cover):
   <div class="media-body">
     <div class="result-body">
       <div>
-        <a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="title getFull" data-view="<?=$this->params->getOptions()->getListViewOption()?>" lang="">
+        <a id="<?=$describedById?>" href="<?=$this->recordLink()->getUrl($this->driver)?>" class="title getFull" data-view="<?=$this->params->getOptions()->getListViewOption()?>" lang="">
           <?=$this->record($this->driver)->getTitleHtml()?>
         </a>
       </div>
diff --git a/themes/finc/templates/header.phtml b/themes/finc/templates/header.phtml
index 50d239b701eb65722cd1115f6bdc0c405fae1650..67b8f3acd01568715ac098fdb5c6741fe52f82d9 100644
--- a/themes/finc/templates/header.phtml
+++ b/themes/finc/templates/header.phtml
@@ -109,7 +109,7 @@
                   <?php foreach ($this->layout()->allLangs as $langCode => $langName): ?>
                     <?php if ($langCode !== $this->layout()->userLang) : ?>
                       <li>
-                        <button type="submit" class="btn <?=(count($this->layout()->allLangs) == 2) ? ' btn-secondary' : ''?>" href="#" onClick="document.langForm.mylang.value='<?=$langCode?>';document.langForm.submit()">
+                        <button type="submit" class="btn <?=(count($this->layout()->allLangs) == 2) ? ' btn-secondary' : ''?>" data-href="#" onClick="document.langForm.mylang.value='<?=$langCode?>';document.langForm.submit()">
                           <span class="visible-sm-md-only"><?=$langCode?></span>
                           <span class="hidden-sm-md"><?=$this->displayLanguageOption($langName)?></span>
                         </button>
@@ -127,8 +127,8 @@
   <?php /* finc searchbox: we use searchbox here so it becomes part of the sticky header,
         we need to place this after the navbar-right for anything but mobile  - see flex-container in SCSS:*/ ?>
   <?php if ($this->layout()->searchbox !== false): ?>
-    <div class="search container">
-      <nav class="nav searchbox hidden-print" role="search">
+    <div class="search container" role="search">
+      <nav class="nav searchbox hidden-print">
         <?=$this->layout()->searchbox?>
       </nav>
     </div>
diff --git a/themes/finc/templates/layout/layout.phtml b/themes/finc/templates/layout/layout.phtml
index a269a91e3a942567214f3c2f97c6b620544988bb..8f4a34ab4c74a4cf5262f19e35e5843b65ff16ab 100644
--- a/themes/finc/templates/layout/layout.phtml
+++ b/themes/finc/templates/layout/layout.phtml
@@ -257,7 +257,7 @@ if (!isset($this->layout()->searchbox)) {
 
 <!-- MODAL IN CASE WE NEED ONE -->
 <?php /* move X button to logical pos. in structure + make accessible via tab - CK */ ?>
-<div id="modal" class="modal fade hidden-print" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="modal-title" aria-hidden="true" aria-describedby="modal-description">
+<div id="modal" class="modal fade hidden-print" tabindex="-1" role="dialog" aria-modal="true" aria-hidden="true" aria-describedby="modal-description">
   <div class="modal-dialog">
     <div class="modal-content">
       <button type="button" class="close" data-dismiss="modal" tabindex="0" aria-label="<?= $this->transEsc('CloseModal') ?>">
diff --git a/themes/finc/templates/myresearch/account.phtml b/themes/finc/templates/myresearch/account.phtml
index b1055a8c55baea6e4f76428bbad1ef93d11cedae..f4e40c2f045ce098ba788dd2cc29355671bfa88a 100644
--- a/themes/finc/templates/myresearch/account.phtml
+++ b/themes/finc/templates/myresearch/account.phtml
@@ -11,7 +11,6 @@
 <?=$this->flashmessages()?>
 
 <form method="post" name="accountForm" id="accountForm" class="form-user-create" data-toggle="validator" role="form">
-  <legend class="sr-only"><?=$this->transEsc('form-legend')?></legend>
   <?=$this->auth()->getCreateFields()?>
   <?=$this->recaptcha()->html($this->useRecaptcha) ?>
   <div class="form-group">
diff --git a/themes/finc/templates/myresearch/newpassword.phtml b/themes/finc/templates/myresearch/newpassword.phtml
index b2102e7315a77aac2af970fed31039ebc427488a..e493278a4bcba753d7aed4bbb79edfc44bffe16a 100644
--- a/themes/finc/templates/myresearch/newpassword.phtml
+++ b/themes/finc/templates/myresearch/newpassword.phtml
@@ -23,7 +23,6 @@
   <div class="error"><?=$this->transEsc('recovery_user_not_found') ?></div>
 <?php else: ?>
   <form id="newpassword" class="form-new-password" action="<?=$this->url('myresearch-newpassword') ?>" method="post" data-toggle="validator" role="form">
-    <legend class="sr-only"><?=$this->transEsc('form-legend')?></legend>
     <input type="hidden" value="<?=$this->escapeHtmlAttr($this->auth()->getManager()->getCsrfHash())?>" name="csrf"/>
     <input type="hidden" value="<?=$this->escapeHtmlAttr($this->hash) ?>" name="hash"/>
     <input type="hidden" value="<?=$this->escapeHtmlAttr($this->username) ?>" name="username"/>
diff --git a/themes/finc/templates/record/cart-buttons.phtml b/themes/finc/templates/record/cart-buttons.phtml
index edb7d83ea32988fbf3970619cf617a6174abcc5b..beaa7cbe11028a7347ce015293c8cd923e53f0b9 100644
--- a/themes/finc/templates/record/cart-buttons.phtml
+++ b/themes/finc/templates/record/cart-buttons.phtml
@@ -3,7 +3,7 @@
 <?php if ($cart->isActive()): ?>
 
     <?php $cartId = $this->source . '|' . $this->id; ?>
-    <span class="btn-bookbag-toggle" data-cart-id="<?=$this->escapeHtmlAttr($this->id)?>" data-cart-source="<?=$this->escapeHtmlAttr($this->source)?>">
+    <div class="btn-bookbag-toggle" data-cart-id="<?=$this->escapeHtmlAttr($this->id)?>" data-cart-source="<?=$this->escapeHtmlAttr($this->source)?>">
       <?php /* Make add-to/remove-from bookbag accessible for keyboard navigation - CK */ ?>
     <a class="cart-add hidden<?php if (!$cart->contains($cartId)): ?> correct<?php endif ?>" href="javascript:" tabindex="0">
       <i class="cart-link-icon fa fa-plus" aria-hidden="true"></i><span class="cart-link-label"><?=$this->transEsc('Add to Book Bag')?></span>
@@ -11,16 +11,17 @@
     <a class="cart-remove hidden<?php if ($cart->contains($cartId)): ?> correct<?php endif ?>" href="javascript:" tabindex="0">
       <i class="cart-link-icon fa fa-minus-circle" aria-hidden="true"></i> <span class="cart-link-label"><?=$this->transEsc('Remove from Book Bag')?></span>
     </a>
-    <noscript>
-      <form method="post" name="addForm" action="<?=$this->url('cart-processor')?>">
+    <form class="cartProcessorNoJs" method="post" name="addForm" action="<?=$this->url('cart-processor')?>">
+      <noscript>
         <input type="hidden" name="ids[]" value="<?=$this->escapeHtmlAttr($cartId)?>"/>
         <?php if ($cart->contains($cartId)): ?>
           <input class="btn btn-default" type="submit" name="delete" value="<?=$this->transEsc('Remove from Book Bag')?>"/>
         <?php else: ?>
           <input class="btn btn-default" type="submit" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"/>
         <?php endif; ?>
-      </form>
-    </noscript>
-  </span>
+      </noscript>
+    </form>
+    <script>$(document).ready(function() { $(".cartProcessorNoJs").css('display', 'none'); });</script>
+  </div>
 <?php endif; ?>
 <!-- finc: record - cart-buttons END -->
diff --git a/themes/finc/templates/record/view.phtml b/themes/finc/templates/record/view.phtml
index 1f97d387fad4ff410d80c391850a4220a5bfe5a1..3749c2121c6fc81e1f807f1b38c5e40014cb3a48 100644
--- a/themes/finc/templates/record/view.phtml
+++ b/themes/finc/templates/record/view.phtml
@@ -40,15 +40,17 @@
       <?= $this->record($this->driver)->getCoreMetadata() ?>
       
       <?php if (count($this->tabs) > 0): ?>
-        <a name="tabnav"></a>
+        <?php /* swap deprecated 'name' for 'ID' - CK */ ?>
+        <a id="tabnav"></a>
         <div class="record-tabs">
+          <?php /* DO NOT add 'role=tablist' for accessibility, see #19938 - CK */ ?>
           <ul class="nav nav-tabs">
               <?php foreach ($this->tabs as $tab => $obj): ?>
                   <?php // add current tab to breadcrumbs if applicable:
                   $desc = $obj->getDescription();
                   $tabName = preg_replace("/\W/", "-", strtolower($tab));
                   $tabClasses = ['record-tab', $tabName];
-                  if (0 === strcasecmp($this->activeTab, $tab)) {
+                  if (($isActiveTab = 0 === strcasecmp($this->activeTab, $tab))) {
                       if (!$this->loadInitialTabWithAjax || !$obj->supportsAjax()) {
                           $tabClasses[] = 'active';
                       }
@@ -63,16 +65,27 @@
                       $tabClasses[] = 'noajax';
                   }
                   ?>
+                <?php /* DO NOT add role="tab" BUT DO ADD aria-controls and ID for accessibility --
+                      'aria-selected' (true/false) needs to be set via record.js - CK */ ?>
                 <li class="<?= implode(' ', $tabClasses) ?>" data-tab="<?= $tabName ?>">
-                  <a
-                    href="<?= $this->recordLink()->getTabUrl($this->driver, $tab) ?>#tabnav"<?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)): ?> data-background<?php endif ?>><?= $this->transEsc($desc) ?></a>
+                  <a href="<?= $this->recordLink()->getTabUrl($this->driver, $tab) ?>#tabnav"
+                    <?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)): ?> data-background<?php endif ?>
+                     aria-selected="<?= $isActiveTab ? "true" : "false" ?>"
+                     aria-controls="<?= $tabName ?>"
+                     id="<?= $tabName ?>-tabselector">
+                    <?= $this->transEsc($desc) ?>
+                    <span class="sr-only load-tab-content"><?= $this->transEsc('load_tab_content_hint') ?></span></a>
                 </li>
               <?php endforeach; ?>
           </ul>
 
-          <div class="tab-content">
+          <div class="tab-content" aria-live="polite" tabindex="-1">
               <?php if (!$this->loadInitialTabWithAjax || !isset($activeTabObj) || !$activeTabObj->supportsAjax()): ?>
-                <div class="tab-pane active <?= $this->escapeHtmlAttr($this->activeTab) ?>-tab">
+                <?php /* Add ID, role and aria-labelledby for accessibility - CK */ ?>
+                <div class="tab-pane active <?= $this->escapeHtmlAttr($this->activeTab) ?>-tab"
+                     role="tabpanel"
+                     id="<?= $this->escapeHtmlAttr($this->activeTab) ?>"
+                     aria-labelledby="<?= $this->activeTab ?>-tabselector">
                     <?= isset($activeTabObj) ? $this->record($this->driver)->getTab($activeTabObj) : '' ?>
                 </div>
               <?php endif; ?>
diff --git a/themes/finc/templates/search/results.phtml b/themes/finc/templates/search/results.phtml
index de9ade6fd08919a365af15829592376107b95f24..8bac2e91e997dee739e6556a1349061de5c306b5 100644
--- a/themes/finc/templates/search/results.phtml
+++ b/themes/finc/templates/search/results.phtml
@@ -85,15 +85,15 @@ $this->headScript()->appendFile("check_save_statuses.js");
     <?php if ($recordTotal > 0): ?>
     <?php /* finc: use spans for easier to show/hide choices - CK */ ?>
       <div class="search-controls">
-        <span class="limit">
+        <div class="limit">
         <?=$this->render('search/controls/limit.phtml')?>
-        </span>
-        <span class="sort right">
+        </div>
+        <div class="sort right">
         <?=$this->render('search/controls/sort.phtml')?>
-        </span>
-        <span class="view">
+        </div>
+        <div class="view">
         <?=$this->render('search/controls/view.phtml')?>
-        </span>
+        </div>
       </div>
     <?php endif; ?>
   </div>