Here’s a quick, high-impact QA pass for your Science.gov page. I focused on correctness, accessibility (508/WCAG), performance, and security—with drop-in fixes.


🔴 Correctness / Robustness

  1. Hero background URL should be root-relative
div#hero {
  /* current: url("images/SciGov_hero_1920.webp") */
  background: #fff url("/images/SciGov_hero_1920.webp");
  background-size: cover;
}

Reason: relative "images/..." will 404 on nested routes (e.g., /about.html).

  1. Use GET for search (shareable/bookmarkable results) + semantics
<form id="search_form" action="https://www.science.gov/scigov/search.html" method="GET" role="search">
  <label for="fullRecord" class="sr-only">Search</label>
  <input type="search" id="fullRecord" name="fullRecord"
         placeholder="Search for authoritative federal science information"
         required autocomplete="on" inputmode="search" aria-label="Search Science.gov">
  <button class="search-btn" type="submit" aria-label="Submit search">
    <span class="material-symbols-outlined search" aria-hidden="true">search</span>
  </button>
  …
</form>
  1. Replace JS navigation button with a link (or add fallback)
<!-- Better semantics & graceful fallback: -->
<a class="btn learn-more" href="/about.html" role="button">Learn More</a>

(If you keep the <button onclick=…>, add a nested <a href="/about.html"> for non-JS environments.)


🟠 Accessibility (508/WCAG quick wins)

  1. “Here’s how you know” disclosure should expose state
<input id="how-you-know" type="checkbox" hidden>
<div id="official-site">
  …
  <label for="how-you-know"
         role="button"
         aria-controls="official-site-info"
         aria-expanded="false">Here's how you know …</label>
</div>

<div id="official-site-info" hidden> … </div>

<script>
  const box = document.getElementById('how-you-know');
  const label = document.querySelector('label[for="how-you-know"]');
  const panel = document.getElementById('official-site-info');
  const sync = () => {
    label.setAttribute('aria-expanded', String(box.checked));
    panel.hidden = !box.checked;
  };
  box.addEventListener('change', sync);
  sync();
</script>
  • Don’t rely solely on the checkbox hack for screen readers—sync aria-expanded and hidden.
  1. Mobile menu toggle should also expose state
<label for="hamburger" aria-controls="primary-menu" aria-expanded="false">
  <span class="material-symbols-outlined menu" aria-hidden="true">menu</span>
  <span class="sr-only">Menu</span>
</label>
<input type="checkbox" id="hamburger" hidden>

<ul id="primary-menu" class="menu" hidden>…</ul>

<script>
  const ham = document.getElementById('hamburger');
  const trigger = document.querySelector('label[for="hamburger"]');
  const menu = document.getElementById('primary-menu');
  const syncMenu = () => {
    trigger.setAttribute('aria-expanded', String(ham.checked));
    menu.hidden = !ham.checked;
  };
  ham.addEventListener('change', syncMenu);
  syncMenu();
</script>
  1. Logos used as links: enrich alt text
  • Current alt values are abbreviations only (e.g., alt="USDA"). Prefer full names:
<img src="/images/agencies/USDA.webp" alt="U.S. Department of Agriculture (USDA)">
  1. Decorative icons should be silent
  • Add alt="" aria-hidden="true" to purely decorative images/icons (e.g., caret glyphs) or render as CSS background.
  1. Back-to-top link: label & keyboard visibility
<a id="backToTop" href="#top" aria-label="Back to top">Top</a>
  • When hidden off-screen, also set aria-hidden="true" so tab order is clean.
  1. Section headings for banner info
  • Inside #official-site-info, add headings or aria-labelledby for the two sections so SR users get structure.

⚡ Performance

  1. Lazy-load non-critical images
<img src="/images/agencies/USDA.webp" alt="…" loading="lazy" width="100" height="84">

(Also set width/height to reduce CLS.)

  1. Preconnect for GA
<link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
<link rel="dns-prefetch" href="https://www.googletagmanager.com">
  1. Throttle scroll handler
  • Inline onscroll runs every frame; switch to IntersectionObserver or throttle:
<script>
  (function(){
    const btn = document.getElementById('backToTop');
    const onScroll = () => {
      const show = window.scrollY > 200;
      btn.style.setProperty('right', show ? '0px' : '-100px');
      btn.setAttribute('aria-hidden', String(!show));
    };
    let ticking = false;
    window.addEventListener('scroll', () => {
      if (!ticking) {
        requestAnimationFrame(() => { onScroll(); ticking = false; });
        ticking = true;
      }
    }, { passive: true });
    onScroll();
  }());
</script>
  1. Respect reduced motion
@media (prefers-reduced-motion: reduce) {
  #backToTop { transition: none !important; }
  * { scroll-behavior: auto; }
}

🔐 Security / Privacy

  1. External links with target="_blank"
  • Add rel="noopener noreferrer" to every target="_blank" (CENDI and all agency links, Vulnerability Disclosure Program).
<a href="https://www.cendi.gov/" target="_blank" rel="noopener noreferrer">…</a>
  1. GA4 IP privacy
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-PL8SK8Q34R', { anonymize_ip: true });
</script>
  1. Content Security Policy (next step)
  • If you plan a CSP, move inline scripts to external files or adopt a nonce across all inline <script> blocks.

🧼 Semantics & UX

  1. Landmarks
  • You already have <header>, <main>, <footer>. Add role="navigation" to the nav if you keep multiple navs:
<nav role="navigation" aria-label="Primary">
  …
</nav>
  1. Page title & alignment
  • <title>Science.gov</title> and <h1>Science.gov</h1> match—good. Keep unique, descriptive titles for secondary pages.
  1. Hero CTA contrast
  • Ensure the “Learn More” button meets contrast with its background; if it’s the browser default, style it to at least a 3:1 contrast for large text or 4.5:1 for normal.
  1. Fallbacks for WebP
  • If you need to support older browsers, wrap hero/logo in <picture> with a JPEG/PNG fallback:
<picture>
  <source srcset="/images/SciGov_hero_1920.webp" type="image/webp">
  <img src="/images/SciGov_hero_1920.jpg" alt="" aria-hidden="true">
</picture>

Quick diff summary

  • ✅ Root-relative hero image URL
  • ✅ Search form method="GET", role="search", accessible labels
  • aria-expanded/hidden sync for banner + mobile menu
  • ✅ Enrich alt text for agency logos; silence decorative icons
  • ✅ Lazy-load images + add width/height
  • ✅ Throttled scroll handler / passive listener; back-to-top aria-hidden
  • rel="noopener noreferrer" on target="_blank"
  • ✅ GA anonymize_ip, preconnect for GTM
  • ✅ prefers-reduced-motion CSS

If you want, I can roll these into a minimal PR (one JS file for toggles + scroll, one CSS patch, and small HTML tweaks) without changing layout or design.