agentleFS
carlosuhartcarlosuhart/seo-audit-skills/skills/image-optimization/SKILL.md

image-optimization

Skill6 starsChanged 12 days agoMITView on GitHub

Image SEO and Core Web Vitals: WebP and AVIF, srcset and sizes, lazy loading, the LCP image (fetchpriority, preload), compression and WordPress or PrestaShop implementation. Use when auditing images for LCP, CLS, weight or accessibility.

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

10 rules. About 0 are common, 8 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈8

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Always keep JPEG/PNG as fallback via <picture>.

    line 29
  2. Distinctive:

    All images that are not visible without scrolling on initial load

    line 138
  3. Very specific:

    Rule: Use <img> for content images.

    line 229
  4. Distinctive:

    If a hero background-image must remain as CSS, add a manual preload:

    line 232
  5. Distinctive:

    When auditing or proposing a gallery, recommend user-controlled zoom: opens on click or tap, shows the image large, zooms with buttons, …

    line 322
  6. Distinctive:

    Only catches images referenced in raw HTML (no JavaScript-injected images)

    line 359
  7. Distinctive:

    Does not follow srcset or picture sources — audits the default src only

    line 360
  8. Distinctive:

    When a site has missing or generic alt text, use Claude's vision capability to analyze the actual image and generate contextually correct …

    line 367
  9. Distinctive:

    Check surrounding page context (H1, page topic, business type)

    line 375
  10. Distinctive:

    Add localization or brand when the image shows a location, team, or branded element

    line 390

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Image Optimization — SEO & Core Web Vitals

Images are 50-70% of page weight on most sites. They are also the most common LCP element. Wrong format, wrong loading strategy, or missing dimensions are among the top 5 CWV failure causes.


Format selection

FormatBest forBrowser supportTypical savings vs JPEG
JPEGPhotos, complex imagesUniversalbaseline
PNGGraphics with transparencyUniversalbaseline
WebPPhotos + graphics95%+ (2024)25-35% smaller
AVIFPhotos + graphics80%+ (2024)40-50% smaller
SVGIcons, logos, illustrationsUniversalN/A (vector)

Recommendation: Serve WebP as the default for all new images. Use AVIF where browser support is acceptable and compression gains justify the encoding time. Always keep JPEG/PNG as fallback via <picture>.

<!-- AVIF with WebP fallback and JPEG as final fallback -->
<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg" alt="Hero image description" width="1200" height="600"
       loading="eager" fetchpriority="high">
</picture>

File size targets

Image typeContextTarget size
LCP heroAbove the fold, full-width< 100 KB
Product imageE-commerce main image< 150 KB
Blog featured imageArticle header< 80 KB
ThumbnailCard, listing< 30 KB
Icon / small graphic< 50pxUse SVG or inline
Background textureDecorative< 50 KB

Sizes above are for WebP. JPEG equivalents are approximately 30-40% larger.


Responsive images — srcset and sizes

Without responsive images, a mobile user downloads a 2400px-wide desktop image for a 390px-wide screen. This wastes bandwidth and delays LCP.

<img
  src="product-800.jpg"
  srcset="product-400.webp 400w,
          product-800.webp 800w,
          product-1200.webp 1200w,
          product-1600.webp 1600w"
  sizes="(max-width: 600px) 100vw,
         (max-width: 1200px) 50vw,
         800px"
  alt="Product name"
  width="800"
  height="600"
  loading="lazy">

sizes attribute explained: Tells the browser how wide the image will be displayed before the CSS is parsed. This allows the browser to select the right source from srcset during preload, before layout is known.

  • (max-width: 600px) 100vw → on screens < 600px, image is 100% of viewport width
  • (max-width: 1200px) 50vw → on screens < 1200px, image is 50% of viewport width
  • 800px → on larger screens, image is always 800px wide

Missing sizes attribute: The browser defaults to 100vw and downloads the largest available image on all screen sizes. Equivalent to not using srcset.


LCP images — do not lazy-load

The LCP image is the most important image on the page. Loading strategies that delay its discovery or download directly increase LCP.

Rules for LCP images

  1. Never loading="lazy" — lazy loading defers the fetch until the element is near the viewport. For the LCP element, the element IS in the viewport.
  2. Always fetchpriority="high" — signals to the browser that this image should be fetched at high priority, ahead of other resources.
  3. Add <link rel="preload"> in <head> — allows the browser to start downloading the image before the HTML is parsed far enough to discover the <img> tag.
  4. Only one fetchpriority="high" per page — setting it on multiple images effectively cancels the priority signal.
<!-- In <head> -->
<link rel="preload" as="image"
      href="/images/hero.webp"
      imagesrcset="/images/hero-400.webp 400w, /images/hero-800.webp 800w"
      imagesizes="(max-width: 600px) 100vw, 800px"
      fetchpriority="high">

<!-- In <body> — the actual LCP image -->
<img src="/images/hero.webp"
     srcset="/images/hero-400.webp 400w, /images/hero-800.webp 800w"
     sizes="(max-width: 600px) 100vw, 800px"
     alt="Hero description"
     width="800" height="400"
     loading="eager"
     fetchpriority="high">

Lazy loading — when it helps vs hurts

loading="lazy" is correct and recommended for all images below the fold. It is incorrect and harmful on images above the fold (especially the LCP image).

When to use loading="lazy"

  • All images that are not visible without scrolling on initial load
  • Product images in a grid below the hero
  • Blog post thumbnails in a list
  • Images in footer sections

When NOT to use loading="lazy"

  • LCP image (hero, product main image)
  • Any image visible in the initial viewport on typical screen sizes
  • Images with fetchpriority="high"

Lazy loading paradox with page builders

Elementor and WP Rocket apply lazy loading automatically to all images, including the LCP hero. This is the most common LCP failure in WordPress sites.

Elementor: The lazy load CSS hides backgrounds of sections 4+ and swaps src with a placeholder. Fix: e-no-lazyload class on hero section.

WP Rocket: Replaces <img src> with data-lazy-src even if loading="eager" or fetchpriority="high" is set in the HTML. Fix: exclude the hero image filename in WP Rocket > Media > LazyLoad > Excluded images.


alt text — SEO and accessibility

<!-- Correct: descriptive, with keyword where natural -->
<img src="limpieza-cristales-madrid.webp"
     alt="Equipo de limpieza de cristales en fachada de edificio en Madrid"
     width="800" height="533">

<!-- Wrong: keyword-stuffed -->
<img src="limpieza.webp" alt="limpieza limpieza cristales limpieza madrid limpieza profesional">

<!-- Wrong: empty alt on content image -->
<img src="team.jpg" alt="">

<!-- Correct: decorative image (pure visual) has empty alt -->
<img src="divider-wave.svg" alt="" role="presentation">

Rules:

  • Content images (photos, product images, infographics): descriptive alt required
  • Decorative images (separators, backgrounds converted to <img>): alt=""
  • Logos: alt="Company name logo"
  • CTA buttons with image: describe the action, not the image

SEO impact: Alt text is used by Google to understand image content and as a ranking signal for image search. It is also a mandatory WCAG accessibility requirement.


Image dimensions — preventing CLS

Missing width and height attributes on images cause layout shifts (CLS) when the image loads and the browser reflows the page to accommodate the image dimensions.

<!-- Causes CLS: browser does not know height before image loads -->
<img src="product.jpg" alt="Product">

<!-- No CLS: browser reserves exact space -->
<img src="product.jpg" alt="Product" width="600" height="400">

CSS alternative:

img {
  aspect-ratio: 3 / 2;  /* matches 600x400 ratio */
  width: 100%;
  height: auto;
}

Both approaches are valid. The aspect-ratio CSS approach is more flexible for responsive images where the intrinsic size varies.


CSS background-image vs <img> tag

CSS background-image<img> tag
Discoverable by preload scannerNoYes
Lazy loadableVia Intersection Observer JSNative loading="lazy"
Accessible (alt text)NoYes
fetchpriority supportNoYes
CLS prevention (dimensions)Via aspect-ratio or min-heightwidth + height attributes

Rule: Use <img> for content images. Use CSS background-image for decorative backgrounds that have no informational value.

If a hero background-image must remain as CSS, add a manual preload:

<link rel="preload" as="image" href="/images/hero-bg.webp" fetchpriority="high">

Compression and optimization workflow

WordPress

ShortPixel (recommended):

  • Bulk optimization with WebP/AVIF generation
  • Lossy compression with quality 82-88 (imperceptible quality loss)
  • CDN delivery option

Smush:

  • Free tier covers most small sites
  • WebP conversion requires Pro plan

Imagify:

  • Aggressive compression presets
  • WebP conversion included

WP Rocket + Imagify: WP Rocket has native integration with Imagify for on-the-fly WebP conversion via CDN rewriting.

PrestaShop

PrestaShop regenerates thumbnails from original uploads. Large originals are stored and thumbnails generated per context (product list, product page, email).

Fix for large originals: reduce upload quality before uploading, or use a module that compresses originals on upload.

PrestaShop thumbnail configuration: Back Office > Design > Image Settings. Regenerate thumbnails after changing sizes.

Manual optimization tools

  • Squoosh (squoosh.app): browser-based, side-by-side comparison, WebP/AVIF
  • ImageOptim (Mac): lossless and lossy compression
  • Sharp (Node.js): programmatic batch conversion
  • cwebp: CLI tool for WebP conversion

CMS-specific issues

WordPress + Elementor — wrong element gets fetchpriority

Elementor Pro's "Image" widget has an "Attributes" field. It is common to see fetchpriority="high" applied manually to a decorative divider or separator icon instead of the hero image. Check that fetchpriority="high" is on the visually largest above-the-fold element.

WordPress — attachments page

WordPress automatically creates a public page for each uploaded image at /domain.com/attachment/filename-of-image/. These pages have thin content (just the image) and should be redirected or set to noindex.

Fix in Yoast: SEO > Search Appearance > Media > Redirect attachment URLs to the attachment itself: Yes.

PrestaShop — product image alt text

PrestaShop uses the product name as the alt text for product images by default. This is often acceptable, but adding specific descriptive alt text per image improves both SEO and accessibility.

The alt text for product images can be set per image in Catalog > Products > Images tab.

WooCommerce — product gallery lazy loading

WooCommerce product gallery images after the first are lazy-loaded by WooCommerce and WP themes. This is correct behavior — they are not visible until the user interacts with the gallery. The main product image should be loading="eager".


Product galleries — hover zoom is a UX finding

Hover zoom (the magnified view follows the cursor) takes control away from the shopper: the zoom can't be held on the part they want to read (label, ingredients, certification marks), it disappears when the cursor moves toward the price or the buy button, and it does not work on mobile, touch or keyboard (WCAG 1.4.13, content on hover or focus). If the product page doesn't let people see the product, they go back to the results page or don't buy.

When auditing or proposing a gallery, recommend user-controlled zoom: opens on click or tap, shows the image large, zooms with buttons, wheel or pinch, pans by dragging, stays open until closed, and loads the large file only when opened (no extra weight on page load). Report an existing hover zoom as a UX problem that hurts conversion and interaction signals. Do not sell it as a direct ranking factor beyond Core Web Vitals.


Diagnostic commands — live site image audit

Detect oversized images (>200 KB) on any URL

curl -s https://example.com/ \
  | grep -oE 'https?://[^"]+\.(jpg|jpeg|png|webp|gif)' \
  | xargs -I {} curl -s -o /dev/null -w "%{size_download} %{url_effective}\n" {} \
  | awk '$1 > 204800 {printf "%.2f KB - %s\n", $1/1024, $2}' \
  | sort -nr

What it does: Fetches the HTML of a URL, extracts all image URLs, measures each image's actual download size, and lists those exceeding 200 KB sorted by size descending.

Variants:

# Lower threshold — flag images > 100 KB (recommended for LCP candidates)
awk '$1 > 102400 {printf "%.2f KB - %s\n", $1/1024, $2}'

# All images regardless of size (full inventory)
awk '{printf "%.2f KB - %s\n", $1/1024, $2}' | sort -nr

# Save to file for reporting
| tee ~/claude-seo/client/image-audit-$(date +%Y%m%d).txt

Limitations:

  • Only catches images referenced in raw HTML (no JavaScript-injected images)
  • Does not follow srcset or picture sources — audits the default src only
  • For JS-rendered pages, combine with a PageSpeed Insights audit or Screaming Frog

Alt text generation — vision workflow

When a site has missing or generic alt text, use Claude's vision capability to analyze the actual image and generate contextually correct alt text.

Workflow

  1. Collect image URLs (from the diagnostic command above or from crawl data)
  2. For each image, fetch and analyze visually:
    • Identify the main subject (person, product, action, location)
    • Check surrounding page context (H1, page topic, business type)
    • Apply keyword + localization/brand where natural
  3. Output format per image:
URL: https://example.com/images/foto1.jpg
Alt propuesto: [descripción natural con keyword y localización si aplica]
Nombre de archivo sugerido: keyword-descripcion-ciudad.webp
Observaciones: [si hay problema de relevancia, duplicado, o imagen decorativa]

Alt text rules (applied during generation)

  • Content images: descriptive alt required — never empty, never keyword-stuffed
  • Include primary keyword naturally where it fits the visual content
  • Add localization or brand when the image shows a location, team, or branded element
  • Decorative images (separators, abstract backgrounds): alt="" — no text
  • Logos: alt="Nombre de empresa logo"
  • Max ~125 characters (screen reader limit)

Filename rules (applied when renaming is possible)

  • kebab-case, no accents, no spaces: limpieza-cristales-madrid.webp
  • Descriptive, not generic: foto1.jpg → equipo-fisioterapia-barcelona.webp
  • Format: .webp, max 100 KB

Example output

URL: https://clinica.es/wp-content/uploads/equipo.jpg
Análisis visual: foto grupal de 4 profesionales con batas blancas en consulta
Contexto página: clínica de fisioterapia en Valencia
Alt propuesto: Equipo de fisioterapeutas de la clínica en Valencia
Nombre sugerido: equipo-fisioterapeutas-valencia.webp

Audit checklist

CRITICAL (LCP impact)
[ ] LCP image: NOT lazy-loaded (no loading="lazy", no data-src placeholder)
[ ] LCP image: has fetchpriority="high"
[ ] LCP image: < 100 KB
[ ] LCP image: served as WebP or AVIF (not JPEG/PNG if avoidable)

HIGH (CLS and performance)
[ ] All content images have explicit width + height (or aspect-ratio CSS)
[ ] Images below the fold use loading="lazy"
[ ] No images > 500 KB on any page (PSI "Properly size images")
[ ] Responsive srcset present on images > 400px wide
[ ] Hero image NOT served as CSS background-image (or preload link present)

MEDIUM (SEO and accessibility)
[ ] All content images have meaningful alt text
[ ] Decorative images have alt="" (empty alt, not missing)
[ ] No keyword-stuffed alt text
[ ] WordPress attachment pages redirected or noindexed
[ ] Product images in e-commerce have descriptive alt text

LOW (optimization)
[ ] WebP/AVIF served to supported browsers
[ ] Image CDN or compression plugin active
[ ] No images loaded unnecessarily on mobile (art direction via <picture>)
[ ] Thumbnails correct size (not oversized images scaled down with CSS)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.