plugins/aem/edge-delivery-services-content-ops/skills/image-seo/SKILL.md
Audit images across AEM Edge Delivery Services pages for SEO and performance. Checks alt text quality, missing dimensions, lazy loading on LCP images, fetch priority, file naming, and decorative image handling. Generates a per-page report with specific fix instructions for alt text (in the source document) and performance attributes (in block code or scripts). Use when optimizing Core Web Vitals, improving image accessibility, or auditing image SEO.
npx skillsauth add adobe/skills image-seoInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Analyze images on AEM Edge Delivery Services pages for alt text quality, performance attributes, file naming, and accessibility compliance, then generate specific fix instructions that distinguish between author fixes (in the source document) and developer fixes (in block code or scripts).
This skill fetches external web pages for analysis. When fetching:
.plain.html variants).In Edge Delivery Services, images are authored in the source document (Google Docs or Microsoft Word). When an author inserts an image, they can set alt text through the image properties in their authoring tool. EDS renders these as standard <img> elements in the HTML.
Key EDS image behaviors:
/.../media_... paths and are automatically converted to WebP, resized, and optimized based on the requesting device. Authors do not need to manually optimize image files.loading="lazy" to images by default. The first image in the first section (typically the LCP image) should not be lazy-loaded. EDS handles this automatically in most cases via its lib-franklin.js or aem.js library, which sets loading="eager" on above-the-fold images.fetchpriority — for the LCP image, fetchpriority="high" signals the browser to download it with high priority. This is typically set in the block's JavaScript code (e.g., hero.js), not by the author.<picture> element with multiple <source> elements for different viewport sizes and formats (WebP). The <img> is the fallback inside <picture>.alt="" (empty alt attribute). In EDS, this requires the author to explicitly set empty alt text in the source document.Important: Use curl or a tool that preserves raw HTML attributes when auditing images. Tools that convert HTML to markdown will strip alt, loading, fetchpriority, width, and height attributes — exactly the attributes this skill needs to inspect.
.plain.html conventions are EDS-specific.Before starting, create a checklist to track progress:
Fetch the page's full HTML using curl or a tool that preserves raw HTML attributes. Do not use .plain.html alone for this skill — the full HTML is needed to check the <head> for preload hints and to see the complete <picture> element structure.
Also fetch .plain.html to identify which images are authored content (vs. images injected by site chrome like header/footer).
For each <img> element in the authored content, extract:
| Attribute | Description |
|-----------|-------------|
| src | The image source URL |
| alt | The alt text (may be empty string, missing, or populated) |
| width | The width attribute |
| height | The height attribute |
| loading | lazy, eager, or absent |
| fetchpriority | high, low, auto, or absent |
| class | CSS classes (may indicate decorative role) |
Also record:
<picture> sources — note if <source> elements provide WebP or responsive variants.Present a summary table of all images found:
| # | File | Alt Text | Width | Height | Loading | Priority | Section | Block | |---|------|----------|-------|--------|---------|----------|---------|-------| | 1 | /media/hero.jpg | "..." | 1600 | 900 | eager | high | 1 | Hero | | 2 | /media/team.jpg | "..." | 800 | 600 | lazy | -- | 3 | -- | | ... | ... | ... | ... | ... | ... | ... | ... | ... |
For each image, evaluate the alt text against these criteria:
| Check | Pass | Fail | Severity |
|-------|------|------|----------|
| Alt attribute present | alt attribute exists on the <img> | No alt attribute at all | Critical (WCAG failure) |
| Not empty on content images | Non-decorative images have descriptive alt text | alt="" on an image that conveys meaning | High |
| Not the filename | Alt text describes the image content | Alt text is "IMG_2034.jpg" or "screenshot-1.png" | High |
| Not too short | Alt text is 5 or more characters | Alt text is fewer than 5 characters (e.g., "logo") | Medium |
| Not too long | Alt text is 125 characters or fewer | Alt text exceeds 125 characters | Low |
| No "image of" prefix | Alt text describes the content directly | Alt text starts with "image of," "photo of," "picture of," "graphic of" | Medium |
| Descriptive and specific | Alt text describes what the image shows | Alt text is generic ("banner," "photo," "icon") | Medium |
| Contextually relevant | Alt text relates to the surrounding content | Alt text describes the image but not its role on the page | Low |
For each failing image, provide:
| Image | Issue | Current Alt | Suggested Alt | |-------|-------|-------------|---------------| | /media/hero.jpg | Starts with "image of" | "image of a team meeting" | "Engineering team discussing product roadmap in weekly standup" | | /media/chart.png | Too short | "chart" | "Bar chart showing 40% revenue growth from Q1 to Q4 2025" | | /media/IMG_2034.jpg | Filename as alt | "IMG_2034" | "Customer using the mobile app to place an order" |
When suggesting alt text:
Images without explicit width and height attributes cause layout shift when they load, hurting Cumulative Layout Shift (CLS) scores.
Flag any <img> missing either width or height:
| Image | Has Width | Has Height | CLS Risk | |-------|-----------|------------|----------| | /media/hero.jpg | Yes (1600) | Yes (900) | None | | /media/sidebar.png | No | No | High |
In EDS, width and height should be set automatically. If they are missing, this may indicate a problem with the image in the source document or the EDS rendering pipeline.
The Largest Contentful Paint (LCP) image is typically the first large image visible on the page — usually in section 1 or the Hero block. This image must NOT have loading="lazy", which delays its download.
Check:
| Check | Pass | Fail |
|-------|------|------|
| LCP image has loading="eager" (or no loading attribute) | Browser fetches immediately | loading="lazy" on LCP image delays rendering |
| LCP image has fetchpriority="high" | Browser prioritizes this download | Missing fetchpriority means default priority |
| LCP image is preloaded in <head> | <link rel="preload" as="image"> found | No preload hint for LCP image |
A lazy-loaded LCP image is the single most impactful performance mistake and should be flagged as P0 (highest priority).
Images in sections 2 and beyond should have loading="lazy" to defer their download until the user scrolls near them. Flag any below-the-fold image with loading="eager" or missing the loading attribute.
Only the LCP image should have fetchpriority="high". Other above-the-fold images can use default priority. Below-the-fold images should not have fetchpriority="high".
Image file naming affects SEO. Search engines use filenames as a signal for image search rankings. In EDS, images are served through media paths like /media/hero-image.jpg, but the original filename (before EDS processing) influences the path.
Check for:
| Issue | Example | Recommendation |
|-------|---------|----------------|
| Non-descriptive name | IMG_2034.jpg, DSC_0042.jpg | Rename to describe content: team-standup-meeting.jpg |
| No hyphens | teammeeting.jpg, hero_image.jpg | Use hyphens as word separators: team-meeting.jpg |
| Too generic | banner.jpg, photo1.jpg, image.png | Be specific: product-dashboard-overview.jpg |
| Excessively long | the-complete-guide-to-everything-you-need-to-know-about-seo.jpg | Keep to 3-5 descriptive words |
| Contains special characters | héro (1).jpg, image%20copy.jpg | Use lowercase alphanumeric characters and hyphens only |
Note: In EDS, renaming the source file in the document may not change the served URL immediately due to caching. The author should replace the image in the source document with a properly named file.
Decorative images serve no informational purpose — they are purely visual (background patterns, dividers, spacers, decorative icons). These should have alt="" (explicitly empty alt attribute) so screen readers skip them entirely.
Identify images that appear decorative: images inside decorative blocks (dividers, spacers), small icons accompanied by text labels, background-style images, and images where surrounding text already conveys the same information.
Flag decorative images that have descriptive alt text (they should have alt=""), and content images that incorrectly have alt="":
| Image | Current Alt | Should Be | Reason |
|-------|-------------|-----------|--------|
| /media/divider.svg | "decorative line" | alt="" | Purely decorative; screen reader should skip |
| /media/product-screenshot.png | (empty) | "Product dashboard showing real-time analytics" | Content image with informational value |
In EDS, setting empty alt text requires the author to open image properties in Google Docs or Word and clear the alt text field.
For each page, produce a table of all image issues with specific fix instructions, categorized by who fixes them:
These fixes require the content author to edit the image in Google Docs or Word:
| # | Image | Issue | Fix | How |
|---|-------|-------|-----|-----|
| 1 | /media/hero.jpg | Alt starts with "image of" | Change alt to "Engineering team at weekly product standup" | Google Docs: right-click image > Alt text > edit |
| 2 | /media/IMG_2034.jpg | Filename as alt | Replace image with a file named customer-mobile-order.jpg and set alt to "Customer using the mobile app to place an order" | Replace image in doc, then set alt text |
| 3 | /media/divider.svg | Decorative with alt text | Set alt to empty (or mark as decorative) | Google Docs: right-click > Alt text > clear text |
These fixes require changes to the EDS project code:
| # | Image | Issue | Fix | Where |
|---|-------|-------|-----|-------|
| 1 | /media/hero.jpg | Missing fetchpriority="high" | Add fetchpriority="high" to the LCP image | blocks/hero/hero.js — set attribute on the <img> element |
| 2 | /media/hero.jpg | loading="lazy" on LCP | Change to loading="eager" | Check lib-franklin.js or aem.js — the default eager/lazy logic may need adjustment |
| 3 | All below-fold images | Missing loading="lazy" | Ensure loading="lazy" is set on images below section 1 | lib-franklin.js or aem.js — EDS usually handles this automatically |
Compile the findings into a structured report:
Rate the image SEO health: Strong / Needs Improvement / Poor
loading="lazy", missing alt attributes (WCAG failure).The fix instruction tables from Step 6 for each page.
| Problem | Cause | Solution |
|---------|-------|----------|
| All images show loading="lazy" | The fetch may be returning server-rendered HTML before client-side JS adjusts attributes | Check the EDS JavaScript (aem.js or lib-franklin.js) for eager loading logic; it may set loading="eager" at runtime |
| Cannot see image attributes | The fetch tool converts HTML to markdown, stripping attributes | Use curl to get raw HTML: curl -s <url> |
| Images are wrapped in <picture> | EDS standard behavior — <source> elements provide responsive variants | Audit the <img> fallback inside <picture>; that is where alt, loading, and fetchpriority belong |
| Image paths are hashed (/media/abc123...) | EDS processes images through its media pipeline | The hash path is expected; focus on the alt text and attributes, not the path |
| Author cannot find alt text setting | Different versions of Google Docs/Word have different UI | Google Docs: right-click image > Alt text. Word: right-click > Edit Alt Text |
| fetchpriority not supported in older browsers | Browser compatibility | fetchpriority is a progressive enhancement; it is safe to add and is ignored by unsupported browsers |
curl for image audits. Tools that convert HTML to markdown strip the exact attributes you need to inspect (alt, loading, width, height, fetchpriority). Always fetch raw HTML for image analysis.loading="eager" and fetchpriority="high" on the LCP image happens in block JavaScript or the EDS library. This is not something the author controls.tools
Use the run-workflow MCP to discover, compose, execute, publish, and save Adobe Firefly workflows. TRIGGER when: user asks what actions are available, what the MCP can do, how to process images/video/3D via workflow, wants to build/run/save/publish a workflow, OR pastes any workflow/batch/execution ID. BARE ID (UUID/workflowId/batchId) = INSPECT ONLY — call inspect_run, NEVER run_workflow_submit. ALWAYS call list_actions first for capability/discovery questions. DO NOT TRIGGER for direct Firefly API calls without MCP (use firefly-api-specs).
tools
Run predefined featured workflows via run-workflow MCP. TRIGGER when user names a featured workflow (retargeting, banners at scale, localization, packaging, banner advertising, etc.) or asks to run a known marketing/production workflow. Requires run-workflow MCP. ALWAYS call get_featured_workflow before compose_workflow. DO NOT TRIGGER for custom one-off workflows with no named template — use run-workflow skill.
tools
Migrate an Adobe Commerce App Builder project from the Integration Starter Kit or Checkout Starter Kit to the new App Management approach. Run from the root of the App Builder project to be migrated. Pass --auto to skip confirmation prompts (suitable for CI or batch use) — auto mode prints a summary of all Q&A questions answered with their defaults. Pass --doc-scan-only to scan README.md and env.dist for outdated content without modifying any files. Use when the user wants to migrate an App Builder project from the Integration Starter Kit or Checkout Starter Kit to the App Management approach, or mentions upgrading their Adobe Commerce extension architecture.
development
Add or modify webhook interceptors in an Adobe Commerce app. Use when the user wants to intercept Commerce operations to validate input, append data, or modify behavior — before or after execution. Requires a base app initialized with commerce-app-init.