# Why Non-Composited Animation Audit Exists

Scans CSS for transitions / animations on non-GPU properties (color, box-shadow, top/left). Finds the repaint-heavy ones.

Author: J.A. Watte
Published: April 23, 2026
Source: https://jwatte.com/blog/blog-tool-non-composited-animation-audit/

---

**TL;DR.** Core Web Vitals — LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1 — are a Google ranking factor. Field data (CrUX, 28-day rolling real-user) beats lab data for this reason.

The **[Non-Composited Animation Audit](/tools/non-composited-animation-audit/)** is the audit you reach for when you already suspect a problem in this dimension and need a fast, copy-paste-able fix list. It reuses the same chrome as every other jwatte.com tool — deep-links from the mega analyzers, AI-prompt export, CSV/PDF/HTML download — but the checks it runs are narrow and specific to the dimension described above.

> Parses CSS (inline <style> + external stylesheets) for transition and animation properties that target non-GPU-composited properties (color, box-shadow, border, top/left/right/bottom instead of transform). These cause main-thread repaints = jank + CLS.

## Why this dimension matters

Core Web Vitals (LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1) are a Google ranking factor. They also gate how often a page can appear in AI Overviews — slow pages get deprioritized in the answer-generation step after the retrieval step. Field data (CrUX, 28-day rolling real-user) beats lab data (Lighthouse) for this reason: Google indexes field metrics, not synthetic ones.

## Common failure patterns

- **LCP hero image with `loading="lazy"`** — lazy-loading the LCP element defers it beyond the above-the-fold paint. The rule is: LCP image = no lazy, add `fetchpriority="high"`, preload it in `<link rel="preload" as="image">` with the right `imagesrcset` for responsive variants.
- **CLS from late-loading web fonts** — `font-display: swap` without `size-adjust` / `ascent-override` fallback produces a layout shift at font load. Use `font-display: optional` on non-critical fonts, or match metrics precisely with a fallback stack.
- **INP from long tasks on interaction** — a single `main-thread` task over 50ms blocks next-paint. The fix is breaking up handlers with `requestIdleCallback`, `scheduler.yield()`, or post-message loops. Third-party analytics scripts are the most common culprit; `defer` + `Partytown` / WebWorker offload helps.
- **TTFB regressions from cold serverless** — scheduled functions with per-region cold starts add 300–1500ms to every geographically-distant visitor. Edge functions (Cloudflare Workers, Vercel Edge, Netlify Edge Functions) cut this to ~50ms.

## How to fix it at the source

Treat CrUX field data as the authoritative measure; Lighthouse lab data is a proxy. Instrument your own RUM (the `web-vitals` library emits LCP/CLS/INP/TTFB/FCP to any endpoint) for percentile breakdowns beyond CrUX's 28-day window. For images: preload the LCP, lazy-load everything below the fold, always provide intrinsic width/height. For scripts: `defer` or `async` or Web Worker — never sync at the top of `<head>`.

## Thresholds that matter

| Signal | Target |
|---|---|
| LCP (Largest Contentful Paint) | Good ≤ 2500ms · Needs Improvement 2500–4000ms · Poor > 4000ms (p75 at origin) |
| INP (Interaction to Next Paint) | Good ≤ 200ms · Needs Improvement 200–500ms · Poor > 500ms (p75) |
| CLS (Cumulative Layout Shift) | Good ≤ 0.1 · Needs Improvement 0.1–0.25 · Poor > 0.25 |
| TTFB (Time to First Byte) | Good ≤ 800ms · Needs Improvement 800–1800ms · Poor > 1800ms |
| FCP (First Contentful Paint) | Good ≤ 1800ms · Needs Improvement 1800–3000ms · Poor > 3000ms |

## Example fix

_LCP hero image — minimal fix:_

```html
<!-- Fail: lazy on LCP, no priority hint, no preload -->
<img src="/images/hero.webp" loading="lazy" alt="Hero">

<!-- Pass: eager, prioritized, preloaded, with srcset for responsive variants -->
<link rel="preload" as="image"
      href="/images/hero-1200.webp"
      imagesrcset="/images/hero-800.webp 800w, /images/hero-1200.webp 1200w, /images/hero-1600.webp 1600w"
      imagesizes="100vw">

<img src="/images/hero-1200.webp"
     srcset="/images/hero-800.webp 800w, /images/hero-1200.webp 1200w, /images/hero-1600.webp 1600w"
     sizes="100vw"
     width="1600" height="900"
     fetchpriority="high"
     decoding="async"
     alt="Hero image of the product in use">
```

## When to run the audit

- After a major site change — redesign, CMS migration, DNS change, hosting platform swap.
- Quarterly as part of routine technical hygiene; the checks are cheap to run repeatedly.
- Before an investor / client review, a PCI scan, a SOC 2 audit, or an accessibility-compliance review.
- When a downstream metric drops (rankings, conversion, AI citations) and you need to rule out this dimension as the cause.

## Reading the output

Every finding is severity-classified. The playbook is the same across tools:

- **Critical / red** — same-week fixes. These block the primary signal and cascade into downstream dimensions.
- **Warning / amber** — same-month fixes. Drag the score, usually don't block.
- **Info / blue** — context only. Often what a PR reviewer would flag but that doesn't block merge.
- **Pass / green** — confirmation. Keep the control in place.

Every audit also emits an "AI fix prompt" — paste into ChatGPT / Claude / Gemini for exact copy-paste code patches tied to your specific stack.

## Related tools in this family

- **[Mega Analyzer](/tools/mega-analyzer/)** — kitchen-sink audit — use when you want CWV alongside SEO/schema/security.
- **[CrUX Field Data Probe](/tools/crux-field-data-probe/)** — pulls the Chrome UX Report directly for origin + URL p75 readings.
- **[Image LCP Candidate Audit](/tools/image-lcp-candidate-audit/)** — identifies which image is the LCP element and whether it's properly prioritized.
- **[Font Loading Strategy Audit](/tools/font-loading-strategy-audit/)** — detects CLS-causing font-load patterns (the #2 CLS source after ads).
- **[Critical CSS Inlining Audit](/tools/critical-css-inline-audit/)** — sibling performance audit — focuses on render-blocking CSS.

## Fact-check notes and sources

- Web.dev: [Core Web Vitals](https://web.dev/articles/vitals)
- Chrome UX Report: https://developer.chrome.com/docs/crux
- Addy Osmani: [Image optimization recipes](https://web.dev/articles/fast)
- W3C: [Resource Hints spec](https://www.w3.org/TR/resource-hints/) — preconnect, preload, prefetch
- GoogleChromeLabs: [web-vitals library](https://github.com/GoogleChromeLabs/web-vitals) — RUM instrumentation

*This post is informational and not a substitute for professional consulting. Mentions of third-party platforms in the tool itself are nominative fair use. No affiliation is implied.*


---

Canonical HTML: https://jwatte.com/blog/blog-tool-non-composited-animation-audit/
RSS: https://jwatte.com/feed.xml
JSON Feed: https://jwatte.com/feed.json
Hero image: https://jwatte.com/images/blog-tool-non-composited-animation-audit.webp
