🔴 High-impact bugs & breakages
- ReferenceError:
DOMPurifyis used but never loaded- Your GSAP line animation script calls
DOMPurify.sanitize(...), but the library isn’t included on the page. This will throw and can short-circuit the block. - Fix: add DOMPurify before any code that calls it:
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js" integrity="sha384-5GzHIkqhHDo+YiH2W5Tq8Pz6VNTGgq7n4GvKJ8h6EB9m1o1rQuc8X9mR97tLWR8c" crossorigin="anonymous"></script>
- Your GSAP line animation script calls
- Duplicate GSAP versions
- You load GSAP 3.11.4 and 3.12.5 (twice) plus
CustomEaselater. Multiple versions = undefined behavior, extra bytes. - Fix: keep one GSAP (latest) and needed plugins, once, after jQuery/Webflow:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/CustomEase.min.js" crossorigin="anonymous"></script>
- You load GSAP 3.11.4 and 3.12.5 (twice) plus
- Broken/placeholder CSS
-webkit-perspective:<some value>is invalid and shipped. Also-webkit-transform-style: preserveis invalid (should bepreserve-3d).- Fix:
.nav-parent, .burger-menu-parent, .flex-cc-h.menu { perspective: 1000px; /* or remove if not needed */ -webkit-backface-visibility: hidden; backface-visibility: hidden; transform-style: preserve-3d; transform: translate3d(0,0,0); }
- Event handlers targeting wrong selectors / never firing
$('.open').on('tap', ...)–tapisn’t a jQuery native event; unless you include a touch library, this will do nothing. Useclick.$('.menu-link-parent').on('mouseenter', function(){ $('.menu-text').find('.menu-text').addClass('hover'); });.menu-textdoesn’t exist in the markup (you havemenu_text-link). This also adds hover to all nodes, not the current one.
- Fix:
$('.open').on('click', function () { $('body').toggleClass('no-scroll'); }); $('.menu-link-parent').on('mouseenter', function () { $(this).find('.menu_text-link').addClass('hover'); }).on('mouseleave', function () { $(this).find('.menu_text-link').removeClass('hover'); });
- Transition hijack can block normal navigation
- You intercept every
<a>click and delay navigation for animations. Edge cases (hash links, modified clicks, downloads, router back/forward) are fragile even with your exceptions. - Fix (safeguards): ignore modifier keys and same-page hash links:
$('a').on('click', function (e) { const a = this; const internal = a.hostname === location.hostname; const hashLink = a.getAttribute('href') && a.getAttribute('href').startsWith('#'); const newTab = a.getAttribute('target') === '_blank'; const modified = e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0; if (!internal || hashLink || newTab || modified) return; e.preventDefault(); // ... trigger transition then window.location = a.href });
- You intercept every
🟠 Performance & Core Web Vitals
- Hero LCP image
- Your right-side hero image is the likely LCP. It’s
loading="eager"(good), but set priority and async decode:<img src=".../President Donald Trump - AI.Gov.jpg" alt="President Donald Trump" width="3000" height="2000" fetchpriority="high" decoding="async"> - Consider a smaller max intrinsic width (e.g., 2000px) unless you truly render at 3000px on desktop.
- Your right-side hero image is the likely LCP. It’s
- Fonts
- You load WebFont Loader (Google) + Google Fonts + Adobe Typekit and then hide scrollbars; that’s a lot.
- Minimize FOIT/FOUT & privacy: either self-host fonts or:
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://use.typekit.net" crossorigin> - Add
font-display: swapfor Google families if you move to CSS@import.
- Third-party script bloat
- You include Splitting CSS but use SplitType JS (different libs). Remove the Splitting CSS includes if you aren’t using Splitting.
lenisis included but not initialized anywhere meaningful—drop it if unused.- Webflow bundles are loaded three times (three different URLs). If these are split chunks that Webflow requires, fine—but ensure there’s no duplicate functional overlap. Audit network waterfall for redundancy.
- Hide scrollbars
::-webkit-scrollbar{width:0}+no-scrollbarclass hurts usability. Don’t hide system scrollbars (accessibility).- Remove those rules or scope to non-essential overflow containers.
- Preconnect critical origins
<link rel="preconnect" href="https://cdn.prod.website-files.com" crossorigin> <link rel="preconnect" href="https://d3e54v103j8qbb.cloudfront.net" crossorigin> <link rel="preconnect" href="https://cdnjs.cloudflare.com"> <link rel="preconnect" href="https://cdn.jsdelivr.net">
🟡 Accessibility (WCAG 2.1 AA quick wins)
- Motion & prefers-reduced-motion
- Heavy GSAP/transitions. Provide a global reduction:
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; scroll-behavior: auto !important; } } - Add a visible “Reduce motion” toggle if brand allows.
- Keyboard & focus
- You’ve added global
:focus-visiblestyles (good!). Ensure burger/menu toggles updatearia-expandedand trap focus when open; restore on close.const btn = document.querySelector('.navbar_menu-button-copy'); const panel = document.querySelector('.menu_parent'); btn.addEventListener('click', () => { const open = btn.getAttribute('aria-expanded') === 'true'; btn.setAttribute('aria-expanded', String(!open)); panel.hidden = open; (open ? document.body.classList.remove : document.body.classList.add)('no-scroll'); });
- Alt text
- Many images have verbose or brand-heavy alts (e.g., includes “AI.Gov”). For content images, keep alts concise and informative; for decorative brand flourishes, use
alt="".
- Headings
- Ensure only one
<h1>per page (your hero looks like it’s images and paragraphs). Make your primary headline a true H1.
- Color contrast
- Verify text over gradients and on gray backgrounds (
#f0f0f0) meets 4.5:1 minimum, especially the small “SEE IT IN ACTION” label and menu hover states.
🔵 SEO & social
- Add
og:image/twitter:image
- You ship title/description cards but no image meta:
<meta property="og:image" content="https://www.ai.gov/path/share.jpg"> <meta property="og:image:width" content="1200"> <meta property="og:image:height" content="630"> <meta name="twitter:image" content="https://www.ai.gov/path/share.jpg"> - Ensure the image is static, ≤2 MB, with text-safe margins.
- Canonical & robots
- You have canonical set; also serve a clean XML sitemap and ensure
robots.txtallows it.
- JSON-LD (Organization + WebSite / SearchAction)
<script type="application/ld+json">
{
"@context":"https://schema.org",
"@type":"GovernmentOrganization",
"name":"AI.gov",
"url":"https://www.ai.gov/",
"logo":"https://www.ai.gov/path/logo.png",
"parentOrganization": {"@type":"GovernmentOrganization","name":"The White House","url":"https://www.whitehouse.gov/"},
"sameAs": ["https://www.whitehouse.gov/"]
}
</script>
🛡️ Security hygiene
- CSP (report-only to start)
- With many CDNs, start permissive and ratchet down:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.prod.website-files.com https://d3e54v103j8qbb.cloudfront.net https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://ajax.googleapis.com https://use.typekit.net https://cdn.jsdelivr.net https://unpkg.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.prod.website-files.com; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com https://use.typekit.net https://cdn.prod.website-files.com; frame-src 'self' https:; connect-src 'self' https:; report-uri https://YOUR-endpoint.example/csp;
rel="noopener noreferrer"
- Add to all
target="_blank"links (many external WhiteHouse links already have it, ensure consistency).
- Mailchimp embed in footer
- Confirm you have a published privacy statement for email collection and that form errors/success are announced to screen readers (add
role="status"to response containers).
🧼 Code hygiene & small fixes
- Remove unused imports
- Splitting CSS (unused), extra GSAP, unused Lenis; drop them.
- Decode images asynchronously
- For all non-LCP images:
<img ... loading="lazy" decoding="async">
- Inline styles with filters/transforms
- Webflow shipped many inline
style="opacity:0;filter:blur(5px);transform:...". That’s fine for initial animations but ensure they are removed (or overridden) once animations complete, so content is visible for users with script disabled / blocked.
- Avoid hiding scrollbars globally
- Remove the global
::-webkit-scrollbar { width: 0 }— use it only on decorative carousels if you must, not the document.
📋 “Ready to ticket” checklist
- Include DOMPurify (or remove its usage).
- Consolidate GSAP to one version; keep only needed plugins.
- Fix invalid CSS values (
<some value>,preserve-3d). - Replace
tapwithclick; fix menu selector.menu_text-link. - Safeguard transition-hijack handler for modified/hash links.
- Mark hero image
fetchpriority="high"anddecoding="async". - Preconnect fonts/CDNs; consider self-hosting fonts.
- Remove Splitting CSS and unused Lenis if not used.
- Add
prefers-reduced-motionCSS. - Ensure burger toggles
aria-expanded/ focus management. - Review image
alttext and heading structure. - Add
og:image/twitter:imageand Organization JSON-LD. - Add CSP (Report-Only) and
rel="noopener noreferrer"to all external links. - Remove global scrollbar-hiding CSS.
- Make Mailchimp responses accessible (
role="status").
If you want, I can turn this into a pull request / Webflow custom code block with the cleaned script and meta bundle, or produce a single “production head” include you can drop into the site settings.