HTML Guides for img
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The sizes attribute works alongside srcset to tell the browser how wide an image will be displayed at various viewport sizes, so the browser can choose the most appropriate image source before the page layout is computed. The attribute value is a comma-separated list where each entry consists of an optional media condition followed by a source size value (a CSS length). The final entry in the list acts as the default size and should not include a media condition.
When the browser parses the sizes attribute and encounters an empty entry — meaning there is nothing meaningful between two commas or after a trailing comma — it cannot resolve that entry to a valid source size. The HTML living standard's parsing algorithm treats this as an error. While most browsers will silently ignore the empty entry and still render the image, the malformed attribute can lead to unexpected behavior and indicates a code quality issue that should be addressed.
Common causes of this error include:
- A trailing comma at the end of the
sizesvalue (e.g.,"100vw, 50vw, "). - Double commas in the middle of the list (e.g.,
"100vw, , 50vw"). - Dynamically generated values where a template or CMS outputs a comma even when a size entry is conditionally omitted.
Fixing the issue is straightforward: ensure every comma in the list separates two valid source size entries, and that the list neither begins nor ends with a comma.
Examples
Trailing comma (invalid)
The most common cause — a comma after the last entry creates an empty source size:
<img
src="photo.jpg"
alt="A mountain landscape"
sizes="(min-width: 1200px) 800px, (min-width: 600px) 400px, 100vw, "
srcset="photo-800.jpg 800w, photo-400.jpg 400w, photo-200.jpg 200w">
Trailing comma fixed
Remove the trailing comma so the list ends with a valid entry:
<img
src="photo.jpg"
alt="A mountain landscape"
sizes="(min-width: 1200px) 800px, (min-width: 600px) 400px, 100vw"
srcset="photo-800.jpg 800w, photo-400.jpg 400w, photo-200.jpg 200w">
Double comma in the middle (invalid)
A double comma creates an empty entry between two valid sizes:
<img
src="banner.jpg"
alt="Promotional banner"
sizes="(min-width: 1024px) 960px, , 100vw"
srcset="banner-960.jpg 960w, banner-480.jpg 480w">
Double comma fixed
Remove the extra comma so each entry is separated by exactly one comma:
<img
src="banner.jpg"
alt="Promotional banner"
sizes="(min-width: 1024px) 960px, 100vw"
srcset="banner-960.jpg 960w, banner-480.jpg 480w">
Dynamically generated sizes with an empty entry (invalid)
Templates or CMS platforms sometimes output commas for entries that are conditionally empty:
<img
src="hero.jpg"
alt="Hero image"
sizes="(min-width: 1400px) 1200px, , (min-width: 768px) 700px, 100vw"
srcset="hero-1200.jpg 1200w, hero-700.jpg 700w, hero-350.jpg 350w">
Dynamically generated sizes fixed
Ensure the template logic only outputs a comma when a valid entry follows:
<img
src="hero.jpg"
alt="Hero image"
sizes="(min-width: 1400px) 1200px, (min-width: 768px) 700px, 100vw"
srcset="hero-1200.jpg 1200w, hero-700.jpg 700w, hero-350.jpg 350w">
If your sizes attribute is built dynamically, consider filtering out empty values before joining them with commas, or trimming trailing commas from the final output. A well-formed sizes attribute should always consist of one or more valid entries separated by single commas, with the last entry serving as the default source size (typically something like 100vw).
The sizes and srcset attributes work together to enable responsive images, but they serve distinct roles and use different syntax. The srcset attribute lists available image files along with their intrinsic widths using the w descriptor (e.g., 800w means the image file is 800 pixels wide). The sizes attribute, on the other hand, tells the browser how wide the image will actually be rendered in the layout, using standard CSS length units. The browser combines this information — knowing which files are available and how large the image will appear — to choose the most efficient file to download.
A common mistake is mixing up these two syntaxes, typically by copying a width descriptor like 860w from srcset and placing it into sizes. Since w is not a CSS unit, the validator rejects it. This matters because an invalid sizes value prevents the browser from correctly calculating which image source to use, potentially causing it to download an unnecessarily large image (wasting bandwidth) or a too-small image (resulting in poor quality).
How the sizes attribute works
The sizes attribute accepts a comma-separated list of media conditions paired with CSS lengths, plus an optional default length at the end. Each entry follows the pattern (media-condition) length. The browser evaluates the conditions in order and uses the length from the first matching condition. If none match, it uses the final default value.
Valid CSS length units include px, em, rem, vw, vh, ch, cm, mm, in, pt, pc, and CSS calc() expressions. You can combine units with calc() for more precise sizing — for example, calc(100vw - 2rem).
Examples
❌ Incorrect: using w in sizes
This triggers the validation error because 860w is a srcset width descriptor, not a CSS length:
<img
alt="A landscape photo"
sizes="860w"
srcset="photo-small.jpg 430w, photo-large.jpg 860w"
src="photo-large.jpg">
✅ Correct: using px in sizes
Replace the w value with a CSS length that reflects the image's actual display size:
<img
alt="A landscape photo"
sizes="860px"
srcset="photo-small.jpg 430w, photo-large.jpg 860w"
src="photo-large.jpg">
✅ Correct: responsive sizes with media conditions
Use media conditions to specify different display sizes at different viewport widths:
<img
alt="A landscape photo"
sizes="(min-width: 1024px) 860px, (min-width: 568px) 430px, 100vw"
srcset="photo-small.jpg 430w, photo-large.jpg 860w"
src="photo-large.jpg">
This tells the browser:
- On viewports 1024px and wider, the image displays at 860px wide.
- On viewports 568px and wider, the image displays at 430px wide.
- On smaller viewports, the image fills the full viewport width (
100vw).
✅ Correct: using calc() in sizes
When the image width depends on padding or margins, calc() provides precise sizing:
<img
alt="A landscape photo"
sizes="(min-width: 768px) calc(50vw - 2rem), calc(100vw - 1rem)"
srcset="photo-small.jpg 400w, photo-medium.jpg 800w, photo-large.jpg 1200w"
src="photo-medium.jpg">
Key takeaways
- The
wdescriptor belongs only insrcset, where it describes the intrinsic width of each image file. - The
sizesattribute uses CSS length units (px,vw,em,calc(), etc.) to describe how wide the image will appear on screen. - If your image always displays at a fixed width, a simple value like
sizes="300px"is sufficient. - If your image width varies by viewport, use media conditions with appropriate CSS lengths to give the browser the information it needs to select the best source.
How the sizes Attribute Works
The sizes attribute works alongside srcset to enable responsive images. When you provide srcset with width descriptors (e.g., 400w, 800w), the browser needs to know how wide the image slot will actually be on screen so it can pick the best candidate. That's what sizes provides — a comma-separated list of size descriptors that tell the browser the rendered width of the image under various viewport conditions.
Each entry in the list follows this pattern:
- Optional media condition followed by a CSS length:
(max-width: 600px) 100vw - A final fallback length with no media condition:
33vw
The browser evaluates media conditions from left to right and uses the length from the first matching condition. If no condition matches, the fallback is used.
Why This Error Occurs
The validator checks that every length token in sizes uses one of the recognized CSS absolute or relative length units: em, ex, ch, rem, cap, ic, vw, vh, vmin, vmax, cm, mm, q, in, pc, pt, px, and their small/large/dynamic viewport variants (svw, lvw, dvw, svh, lvh, dvh, svi, lvi, dvi, svb, lvb, dvb, svmin, lvmin, dvmin, svmax, lvmax, dvmax).
Percentages (%) are not allowed. This is a common point of confusion — while % is valid in most CSS contexts, the sizes attribute explicitly forbids it because a percentage would be ambiguous (percentage of what?). The vw unit is typically the correct replacement when you want to express a fraction of the viewport width.
Here are the most common mistakes that trigger this error:
- Missing units:
sizes="100"— a bare number has no meaning without a unit. - Using percentages:
sizes="50%"— use50vwinstead. - Typos in unit names:
100pxx,100vws,50 vw(with a space between number and unit). - Multiple lengths in a single entry:
sizes="(min-width: 800px) 50vw 400px"— each entry must contain exactly one length. - Using
calc()incorrectly: Whilecalc()is valid insizes, the expressions inside it must also use valid units.
Why It Matters
When the sizes value is malformed, browsers fall back to a default of 100vw, which means every image is treated as if it spans the full viewport width. This defeats the purpose of responsive images — the browser may download unnecessarily large files on small screens, wasting bandwidth and slowing page loads. Valid sizes values are essential for proper image optimization.
Additionally, invalid HTML can cause unpredictable behavior across different browsers and versions. Standards-compliant markup ensures consistent rendering and forward compatibility.
How to Fix It
- Find the position indicated in the error message (the "at Z" part) — this tells you exactly where in the
sizesstring the invalid token was found. - Check for bare numbers and add the appropriate unit (
px,vw,em, etc.). - Replace
%withvwif you intended a percentage of the viewport width. - Fix any typos in unit names.
- Ensure each comma-separated entry has exactly one length, optionally preceded by a media condition in parentheses.
- Verify there's no space between the number and its unit —
100vwis correct,100 vwis not.
Examples
❌ Bare number without a unit
<img
src="photo-400.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="(max-width: 600px) 100, 400"
alt="A landscape photo">
✅ Fixed: add vw and px units
<img
src="photo-400.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="(max-width: 600px) 100vw, 400px"
alt="A landscape photo">
❌ Using invalid percentage
<img
src="banner-800.jpg"
srcset="banner-800.jpg 800w, banner-1600.jpg 1600w"
sizes="(max-width: 700px) 100%, 80%"
alt="Promotional banner">
✅ Fixed: replace % with vw
<img
src="banner-800.jpg"
srcset="banner-800.jpg 800w, banner-1600.jpg 1600w"
sizes="(max-width: 700px) 100vw, 80vw"
alt="Promotional banner">
❌ Multiple lengths in one entry
<img
src="hero-640.jpg"
srcset="hero-640.jpg 640w, hero-1280.jpg 1280w"
sizes="(min-width: 800px) 50vw 400px, 100vw"
alt="Hero image">
✅ Fixed: one length per entry, separated by commas
<img
src="hero-640.jpg"
srcset="hero-640.jpg 640w, hero-1280.jpg 1280w"
sizes="(min-width: 800px) 50vw, 100vw"
alt="Hero image">
❌ Typo in unit name
<img
src="thumb-320.jpg"
srcset="thumb-320.jpg 320w, thumb-640.jpg 640w"
sizes="320pxx"
alt="Thumbnail">
✅ Fixed: correct the unit
<img
src="thumb-320.jpg"
srcset="thumb-320.jpg 320w, thumb-640.jpg 640w"
sizes="320px"
alt="Thumbnail">
✅ Multiple media conditions with a fallback
<img
src="photo-640.jpg"
srcset="photo-640.jpg 640w, photo-960.jpg 960w, photo-1280.jpg 1280w"
sizes="(min-width: 1200px) 800px, (min-width: 800px) 60vw, 90vw"
alt="Landscape photo">
✅ Using calc() with valid units
<img
src="article-img-400.jpg"
srcset="article-img-400.jpg 400w, article-img-800.jpg 800w"
sizes="(min-width: 960px) calc(50vw - 2rem), 100vw"
alt="Article illustration">
The calc() function is valid inside sizes and is useful when the image width depends on a combination of viewport size and fixed spacing like padding or margins. Just make sure every value inside calc() also uses valid units.
Backslashes are not valid delimiters in URLs according to the URL Living Standard. While some browsers may silently normalize backslashes to forward slashes, this behavior is non-standard and should not be relied upon. Using backslashes in URLs can lead to broken images, unexpected behavior across different browsers, and failures in environments that strictly follow URL specifications (such as HTTP servers, CDNs, or validation tools).
This issue commonly arises when developers copy file paths directly from a Windows file system — where \ is the directory separator — and paste them into HTML src attributes. It can also happen when server-side code generates URLs using OS-level path functions instead of URL-building utilities.
Beyond standards compliance, this matters for several practical reasons:
- Cross-browser reliability: Not all browsers or HTTP clients normalize backslashes the same way.
- Server compatibility: Many web servers interpret backslashes literally, resulting in 404 errors.
- Portability: Code with backslash paths may work in local development on Windows but break when deployed to a Linux-based server.
To fix the issue, locate every backslash in the src attribute value and replace it with a forward slash. This applies to all URL contexts, not just img elements — though the validator specifically flags it here.
Examples
❌ Incorrect: backslashes in the src path
<imgsrc="images\photos\landscape.jpg"alt="Mountain landscape">
<imgsrc="https://example.com\img\small\photo.png"alt="Example image">
Both of these use backslashes as path delimiters, which triggers the validation error.
✅ Correct: forward slashes in the src path
<imgsrc="images/photos/landscape.jpg"alt="Mountain landscape">
<imgsrc="https://example.com/img/small/photo.png"alt="Example image">
Simply replacing \ with / resolves the issue and produces a valid, portable URL.
❌ Incorrect: mixed delimiters
<imgsrc="assets/images\banner\hero.webp"alt="Hero banner">
Even a single backslash in an otherwise valid path will trigger this error.
✅ Correct: consistent forward slashes
<imgsrc="assets/images/banner/hero.webp"alt="Hero banner">
Tips to avoid this issue
- Don't copy-paste Windows file paths directly into HTML. Always convert backslashes to forward slashes.
- Use your editor's find-and-replace to search for
\withinsrcattributes across your project. - If generating URLs in server-side code, use URL-building functions rather than file-system path functions. For example, in Node.js, use the
urlmodule or template literals with/instead ofpath.join(), which uses\on Windows. - Run the W3C validator regularly during development to catch issues like this before deployment.
URLs follow strict syntax rules defined by RFC 3986. Within a URL's path segment, only a specific set of characters is allowed to appear literally. When a character falls outside this allowed set, it must be percent-encoded — represented as a % sign followed by two hexadecimal digits corresponding to the character's ASCII code. The W3C validator checks that every URL in your HTML conforms to these rules, and it flags any src value that contains raw illegal characters.
Characters that commonly trigger this error include:
| Character | Percent-encoded |
|---|---|
| (space) | %20 |
[ | %5B |
] | %5D |
{ | %7B |
} | %7D |
| | %7C |
^ | %5E |
` | %60 |
Other reserved characters like ?, #, @, !, $, &, ', (, ), *, +, ,, ;, and = also need encoding when used as literal data in a path segment rather than as URL delimiters. The % character itself must be encoded as %25 if it appears literally.
Why This Is a Problem
- Browser inconsistency: While many modern browsers silently fix malformed URLs, not all do. Some browsers, older user agents, or HTTP clients may fail to load the resource or interpret the URL differently, leading to broken images.
- Standards compliance: Invalid URLs violate the HTML specification, which requires that attribute values containing URLs conform to valid URL syntax.
- Interoperability: Servers, CDNs, proxies, and caching layers may handle illegal characters unpredictably, causing intermittent failures that are difficult to debug.
- Accessibility: If a URL is malformed and the image fails to load, users relying on assistive technologies may not receive the intended content, even when appropriate
alttext is provided.
How to Fix It
You have two main approaches:
- Percent-encode the illegal characters in the
srcvalue. Replace each offending character with its%XXequivalent. - Rename the file to use only URL-safe characters. Stick to lowercase letters, digits, hyphens (
-), underscores (_), and dots (.). This is the cleanest long-term solution.
If you're generating URLs programmatically, use your language's built-in URL encoding functions (e.g., encodeURIComponent() in JavaScript, urlencode() in PHP, or urllib.parse.quote() in Python).
Examples
Illegal characters in the filename
The square brackets in the src value are not allowed in a URL path segment:
<!-- ❌ Invalid: raw [ and ] in URL path -->
<imgsrc="image[00].svg"alt="Company logo">
Fix by percent-encoding:
<!-- ✅ Valid: [ and ] are percent-encoded -->
<imgsrc="image%5B00%5D.svg"alt="Company logo">
Fix by renaming the file:
<!-- ✅ Valid: filename uses only safe characters -->
<imgsrc="image-00.svg"alt="Company logo">
Spaces in the filename
Spaces are one of the most common causes of this error:
<!-- ❌ Invalid: space in URL path -->
<imgsrc="my photo.jpg"alt="Vacation photo">
<!-- ✅ Valid: space encoded as %20 -->
<imgsrc="my%20photo.jpg"alt="Vacation photo">
<!-- ✅ Valid: filename uses a hyphen instead of a space -->
<imgsrc="my-photo.jpg"alt="Vacation photo">
Curly braces in a template-like path
Sometimes filenames or paths contain curly braces from templating artifacts or naming conventions:
<!-- ❌ Invalid: raw { and } in URL path -->
<imgsrc="icons/{home}.png"alt="Home icon">
<!-- ✅ Valid: curly braces percent-encoded -->
<imgsrc="icons/%7Bhome%7D.png"alt="Home icon">
Best practice for file naming
The simplest way to avoid this error entirely is to adopt a consistent file naming convention that only uses URL-safe characters:
<!-- ✅ Valid: clean, URL-safe filenames -->
<imgsrc="images/hero-banner-2024.webp"alt="Welcome banner">
<imgsrc="photos/team_photo_01.jpg"alt="Our team">
Spaces in the src attribute of an img element are not valid URL characters and must be encoded as %20 or replaced with hyphens/underscores.
URLs follow the rules defined in RFC 3986, which does not allow literal space characters in any part of a URL. When a browser encounters a space in a src value, it may try to percent-encode it automatically, but this behavior is not guaranteed across all contexts. The W3C validator flags this because the HTML specification requires attribute values containing URLs to hold valid URL strings.
There are two ways to fix this: rename the file to remove spaces, or percent-encode the spaces as %20. Renaming is the better long term approach because it avoids encoding issues entirely and makes URLs easier to read and share.
Examples
Invalid: space in the file name
<imgsrc="my photo.jpg"alt="A photo">
Valid: percent-encoded space
<imgsrc="my%20photo.jpg"alt="A photo">
Valid: renamed file with no spaces
<imgsrc="my-photo.jpg"alt="A photo">
URLs follow a strict syntax defined by the URL Living Standard and RFC 3986. Only a specific set of characters are allowed to appear literally in a URL's query string. Characters outside this set — such as pipes, square brackets, curly braces, and certain other symbols — must be percent-encoded. Percent-encoding replaces the character with a % sign followed by its two-digit hexadecimal ASCII code.
When the W3C HTML Validator encounters an <img> tag whose src attribute contains an illegal character in the query portion of the URL, it raises this error. The query string is the part of the URL that comes after the ? character.
Why this matters
- Browser inconsistency: While many modern browsers will silently fix malformed URLs, not all browsers or HTTP clients handle illegal characters the same way. Some may misinterpret the URL or fail to load the resource entirely.
- Standards compliance: Valid URLs are a foundational requirement for interoperable web content. Using illegal characters violates both the HTML and URL specifications.
- Interoperability: Automated tools, web crawlers, proxies, and content delivery networks may reject or mangle URLs containing unencoded special characters, leading to broken images.
- Accessibility: Screen readers and assistive technologies rely on valid markup. Malformed URLs can cause unexpected behavior in these tools.
Common illegal characters and their encodings
Here are characters frequently flagged by the validator:
| Character | Percent-encoded |
|---|---|
| (pipe) | %7C |
[ | %5B |
] | %5D |
{ | %7B |
} | %7D |
^ | %5E |
` (backtick) | %60 |
| (space) | %20 |
How to fix it
Identify the illegal characters in the src URL and replace each one with its corresponding percent-encoded value. If you're generating URLs dynamically with a programming language, use the language's built-in URL-encoding function (e.g., encodeURI() or encodeURIComponent() in JavaScript, urlencode() in PHP, urllib.parse.quote() in Python).
Examples
❌ Incorrect: unencoded pipe character in query string
<imgsrc="https://example.com/image?filter=red|blue"alt="Filtered image">
The | character is not allowed literally in the query string.
✅ Correct: pipe character percent-encoded
<imgsrc="https://example.com/image?filter=red%7Cblue"alt="Filtered image">
❌ Incorrect: unencoded square brackets in query string
<imgsrc="https://example.com/image?size[width]=300&size[height]=200"alt="Resized image">
The [ and ] characters must be encoded.
✅ Correct: square brackets percent-encoded
<imgsrc="https://example.com/image?size%5Bwidth%5D=300&size%5Bheight%5D=200"alt="Resized image">
❌ Incorrect: space in query string
<imgsrc="https://example.com/image?caption=hello world"alt="Captioned image">
✅ Correct: space percent-encoded
<imgsrc="https://example.com/image?caption=hello%20world"alt="Captioned image">
Encoding URLs dynamically
If your URLs are built in JavaScript, use encodeURIComponent() for individual query parameter values:
constfilter="red|blue";
consturl=`https://example.com/image?filter=${encodeURIComponent(filter)}`;
// Result: "https://example.com/image?filter=red%7Cblue"
This ensures that any special characters in user-provided or dynamic values are properly encoded before being placed into the HTML.
URLs follow strict syntax rules defined by RFC 3986, which does not allow literal space characters. When the W3C validator encounters a space in the src attribute of an <img> element, it reports it as an illegal character in the scheme data. While most modern browsers will attempt to handle spaces in URLs by automatically encoding them, relying on this behavior is not standards-compliant and can lead to broken images in certain contexts, such as when URLs are processed by other tools, APIs, or older browsers.
This issue commonly arises when file names contain spaces (e.g., my photo.jpg) and the path is copied directly into the HTML. It can also happen when a URL is incorrectly constructed by concatenating strings with unencoded values.
Beyond validation, unencoded spaces can cause real problems. A URL with a space may break when shared, copied, or passed through redirects. Email clients and messaging apps may truncate the URL at the space. Search engine crawlers might fail to index the resource correctly.
How to fix it
There are several ways to resolve this issue:
- Percent-encode spaces as
%20— Replace every literal space in the URL with%20. - Rename the file — Remove spaces from file names entirely, using hyphens (
-), underscores (_), or camelCase instead. - Use proper URL encoding functions — If generating URLs dynamically (e.g., in JavaScript or a server-side language), use built-in encoding functions like
encodeURI()orencodeURIComponent().
Renaming files to avoid spaces is generally the best long-term practice, as it prevents encoding issues across your entire workflow.
Examples
❌ Invalid: spaces in the src attribute
<imgsrc="images/my photo.jpg"alt="A vacation photo">
<imgsrc="/assets/blog posts/header image.png"alt="Blog header">
Both of these will trigger the validation error because the src value contains literal space characters.
✅ Fixed: spaces encoded as %20
<imgsrc="images/my%20photo.jpg"alt="A vacation photo">
<imgsrc="/assets/blog%20posts/header%20image.png"alt="Blog header">
✅ Fixed: file renamed to remove spaces
<imgsrc="images/my-photo.jpg"alt="A vacation photo">
<imgsrc="/assets/blog-posts/header-image.png"alt="Blog header">
A note on + vs %20
You may see spaces encoded as + in some contexts (e.g., query strings in form submissions using application/x-www-form-urlencoded). However, + is not a valid substitute for a space in a URL path. Always use %20 for spaces in the src attribute.
<!-- ❌ Incorrect: + does not represent a space in a URL path -->
<imgsrc="images/my+photo.jpg"alt="A vacation photo">
<!-- ✅ Correct -->
<imgsrc="images/my%20photo.jpg"alt="A vacation photo">
The HTML specification requires that URL attributes like src contain valid URLs. While browsers are generally forgiving and may strip or ignore whitespace characters in URLs, including tabs (\t), newlines (\n), or carriage returns (\r) inside a src value is technically invalid. These characters are not legal within a URL according to both the HTML and URL standards.
Beyond standards compliance, there are practical reasons to fix this:
- Unpredictable behavior: Different browsers may handle embedded whitespace differently. Some might silently strip it, while others could encode it as
%0Aor%0D, leading to broken image requests. - Debugging difficulty: Invisible characters make URLs fail silently. The path looks correct in your editor, but the actual HTTP request goes to a different (malformed) URL.
- Accessibility and tooling: Screen readers, crawlers, and other tools that parse your HTML may not handle malformed URLs gracefully, potentially causing images to be skipped or reported as broken.
Common Causes
This issue typically appears in a few scenarios:
- Multi-line attributes in templates: When a URL is built dynamically in a template engine (e.g., PHP, Jinja, Handlebars), line breaks in the template code can leak into the rendered attribute value.
- Copy-paste errors: Copying a URL from a document, email, or another source may include hidden line breaks or tab characters.
- Code formatting: Some developers or auto-formatters may break long attribute values across multiple lines, inadvertently injecting newlines into the value.
- String concatenation: Building URLs through string concatenation in server-side code can introduce whitespace if variables contain trailing newlines.
Examples
Incorrect: Newline inside src value
The newline before the closing quote makes this an invalid URL:
<imgsrc="images/photo.jpg "alt="A scenic photo">
Incorrect: Tab character in src value
A tab character embedded in the middle of the URL path:
<imgsrc="images/ photo.jpg"alt="A scenic photo">
Incorrect: Multi-line URL from template output
This can happen when a template engine outputs a URL with leading or trailing whitespace:
<imgsrc=" https://example.com/images/photo.jpg "alt="A scenic photo">
https://example.com/images/photo.jpg
Correct: Clean, single-line src value
The URL is a single continuous string with no tabs, newlines, or carriage returns:
<imgsrc="images/photo.jpg"alt="A scenic photo">
Correct: Long URL kept on one line
Even if the URL is long, it must remain on a single line without breaks:
<imgsrc="https://cdn.example.com/assets/images/2024/photos/scenic-landscape-full-resolution.jpg"alt="A scenic photo">
How to Fix It
- Inspect the attribute value: Open the raw HTML source (not the rendered page) and look at the
srcvalue character by character. Use your editor's "show whitespace" or "show invisible characters" feature to reveal hidden tabs and line breaks. - Remove all whitespace inside the URL: Ensure the entire URL is on a single line with no tabs, newlines, or carriage returns anywhere between the opening and closing quotes.
- Check your templates: If the URL is generated dynamically, trim the output. Most languages offer a
trim()orstrip()function that removes leading and trailing whitespace including newlines. - Lint your HTML: Use the W3C validator or an HTML linter in your build pipeline to catch these issues automatically before deployment.
The srcset attribute allows you to provide the browser with a set of image sources to choose from based on the user's viewport size or display density. Each entry in srcset is called an image candidate string and consists of a URL followed by an optional descriptor — either a width descriptor (like 300w) or a pixel density descriptor (like 2x).
The sizes attribute tells the browser what display size the image will occupy at various viewport widths, using media conditions and length values. The browser uses this size information together with the width descriptors in srcset to select the most appropriate image. This is why the HTML specification requires that when sizes is present, all srcset entries must use width descriptors — without them, the browser cannot perform the size-based selection that sizes is designed to enable.
This error typically appears in three situations:
- A
srcsetentry has no descriptor at all — the URL is listed without any accompanying width or density value. - A pixel density descriptor (
x) is used alongsidesizes— mixingsizeswithxdescriptors is invalid because the two mechanisms are mutually exclusive. - A typo or formatting issue — for example, writing
600pxinstead of600w, or placing a comma incorrectly.
Why this matters
- Standards compliance: The WHATWG HTML Living Standard explicitly states that when
sizesis specified, all image candidates must use width descriptors. - Correct image selection: Without proper width descriptors, browsers cannot accurately determine which image to download. This may lead to unnecessarily large downloads on small screens or blurry images on large screens.
- Performance: Responsive images are a key performance optimization. A malformed
srcsetdefeats the purpose and can result in wasted bandwidth.
How to fix it
- Determine the intrinsic width (in pixels) of each image file listed in
srcset. - Append the width descriptor to each URL in the format
[width]w, where[width]is the image's actual pixel width. - Ensure no entries use
xdescriptors whensizesis present. If you need density descriptors, remove thesizesattribute entirely. - Make sure every entry has a descriptor — bare URLs without any descriptor are invalid when
sizesis used.
Examples
Missing width descriptor
This triggers the validation error because the srcset URL has no width descriptor:
<img
src="/img/photo.jpg"
srcset="/img/photo.jpg"
sizes="(max-width: 600px) 100vw, 600px"
alt="A sunset over the mountains"
>
Fixed by adding the width descriptor:
<img
src="/img/photo.jpg"
srcset="/img/photo.jpg 600w"
sizes="(max-width: 600px) 100vw, 600px"
alt="A sunset over the mountains"
>
Using pixel density descriptors with sizes
This is invalid because x descriptors cannot be combined with the sizes attribute:
<img
src="/img/photo.jpg"
srcset="/img/photo.jpg 1x, /img/photo-2x.jpg 2x"
sizes="(max-width: 800px) 100vw, 800px"
alt="A sunset over the mountains"
>
Fixed by switching to width descriptors:
<img
src="/img/photo.jpg"
srcset="/img/photo.jpg 800w, /img/photo-2x.jpg 1600w"
sizes="(max-width: 800px) 100vw, 800px"
alt="A sunset over the mountains"
>
Alternatively, if you only need density-based selection and don't need sizes, remove it:
<img
src="/img/photo.jpg"
srcset="/img/photo.jpg 1x, /img/photo-2x.jpg 2x"
alt="A sunset over the mountains"
>
Multiple image sources with width descriptors
A complete responsive image setup with several sizes:
<img
src="/img/photo-800.jpg"
srcset=" /img/photo-400.jpg 400w, /img/photo-800.jpg 800w, /img/photo-1200.jpg 1200w "
/img/photo-400.jpg 400w,
/img/photo-800.jpg 800w,
/img/photo-1200.jpg 1200w
sizes="(max-width: 480px) 100vw, (max-width: 960px) 50vw, 800px"
alt="A sunset over the mountains"
>
Each URL is paired with a w descriptor that matches the image's intrinsic pixel width. The sizes attribute then tells the browser how wide the image will display at each breakpoint, allowing it to pick the best candidate.
The srcset attribute allows you to specify multiple image sources so the browser can choose the most appropriate one based on the user's device characteristics, such as screen resolution or viewport width. When you include a srcset attribute on an <img> element, the HTML specification requires it to contain one or more comma-separated image candidate strings. Each string consists of a URL followed by an optional descriptor — either a width descriptor (e.g., 200w) or a pixel density descriptor (e.g., 2x).
This validation error typically appears when:
- The
srcsetattribute is empty (srcset="") - The
srcsetattribute contains only whitespace (srcset=" ") - The value contains syntax errors such as missing URLs, invalid descriptors, or incorrect formatting
- A templating engine or CMS outputs the attribute with no value
This matters because browsers rely on the srcset value to select the best image to load. An empty or malformed srcset means the browser must fall back entirely to the src attribute, making the srcset attribute pointless. Additionally, invalid markup can cause unexpected behavior across different browsers and undermines standards compliance.
How to fix it
- Provide valid image candidate strings. Each entry needs a URL and optionally a width or pixel density descriptor, with entries separated by commas.
- Remove the attribute entirely if you don't have multiple image sources to offer. A plain
srcattribute is perfectly fine on its own. - Check dynamic output. If a CMS or templating system generates the
srcset, ensure it conditionally omits the attribute when no responsive image candidates are available, rather than outputting an empty attribute.
Examples
❌ Empty srcset attribute
<imgsrc="/img/photo.jpg"alt="A sunset over the ocean"srcset="">
This triggers the error because srcset is present but contains no image candidate strings.
❌ Malformed srcset value
<imgsrc="/img/photo.jpg"alt="A sunset over the ocean"srcset="1x, 2x">
This is invalid because each candidate string must include a URL. Descriptors alone are not valid entries.
✅ Using pixel density descriptors
<img
src="/img/photo-400.jpg"
alt="A sunset over the ocean"
srcset=" /img/photo-400.jpg 1x, /img/photo-800.jpg 2x ">
/img/photo-400.jpg 1x,
/img/photo-800.jpg 2x
Each candidate string contains a URL followed by a pixel density descriptor (1x, 2x). The browser picks the best match for the user's display.
✅ Using width descriptors with sizes
<img
src="/img/photo-400.jpg"
alt="A sunset over the ocean"
srcset=" /img/photo-400.jpg 400w, /img/photo-800.jpg 800w, /img/photo-1200.jpg 1200w "
/img/photo-400.jpg 400w,
/img/photo-800.jpg 800w,
/img/photo-1200.jpg 1200w
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px">
Width descriptors (e.g., 400w) tell the browser the intrinsic width of each image. The sizes attribute then tells the browser how large the image will be displayed at various viewport sizes, allowing it to calculate the best source to download.
✅ Removing srcset when not needed
<imgsrc="/img/photo.jpg"alt="A sunset over the ocean">
If you only have a single image source, simply omit srcset altogether. The src attribute alone is valid and sufficient.
✅ Single candidate in srcset
<img
src="/img/photo.jpg"
alt="A sunset over the ocean"
srcset="/img/photo-highres.jpg 2x">
Even a single image candidate string is valid. Here, the browser will use the high-resolution image on 2x displays and fall back to src otherwise.
The srcset attribute allows you to provide multiple image sources so the browser can choose the most appropriate one based on the user's viewport size or screen density. There are two distinct modes for srcset:
- Width descriptor mode — each candidate specifies its intrinsic width using a
wdescriptor (e.g.,400w). This mode requires thesizesattribute so the browser knows how much space the image will occupy in the layout and can calculate which source to download. - Pixel density descriptor mode — each candidate specifies a pixel density using an
xdescriptor (e.g.,2x). This mode must not include asizesattribute.
When you include a sizes attribute but forget to add width descriptors to one or more srcset entries, the browser has incomplete information. The HTML specification explicitly states that if sizes is present, all image candidate strings must use width descriptors. An entry without a descriptor defaults to 1x (a pixel density descriptor), which conflicts with the width descriptor mode triggered by sizes. This mismatch causes the W3C validator to report the error.
Beyond validation, this matters for real-world performance. Responsive images are one of the most effective tools for reducing page weight on smaller screens. If the descriptors are missing or mismatched, browsers may download an image that is too large or too small, hurting both performance and visual quality.
How to fix it
You have two options depending on your use case:
Option 1: Add width descriptors to all srcset candidates
If you need the browser to select images based on viewport size (the most common responsive images pattern), keep the sizes attribute and ensure every srcset entry has a w descriptor that matches the image's intrinsic pixel width.
Option 2: Remove sizes and use pixel density descriptors
If you only need to serve higher-resolution images for high-DPI screens (e.g., Retina displays) and the image always renders at the same CSS size, remove the sizes attribute and use x descriptors instead.
Examples
❌ Incorrect: sizes present but srcset entry has no width descriptor
<img
src="photo-800.jpg"
srcset="photo-400.jpg, photo-800.jpg"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
Both srcset entries lack a width descriptor. Because sizes is present, the validator reports an error for each candidate.
✅ Correct: sizes present with width descriptors on every candidate
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
Each candidate now specifies its intrinsic width (400w and 800w), which tells the browser the actual pixel width of each source file. The browser combines this with the sizes value to pick the best match.
❌ Incorrect: mixing width descriptors and bare entries
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
The second candidate (photo-800.jpg) is missing its width descriptor. All candidates must have one when sizes is present — not just some of them.
✅ Correct: pixel density descriptors without sizes
<img
src="photo-800.jpg"
srcset="photo-800.jpg 1x, photo-1600.jpg 2x"
alt="A mountain landscape">
Here the sizes attribute is removed, and each srcset entry uses a pixel density descriptor (1x, 2x). This is valid and appropriate when the image always occupies the same CSS dimensions regardless of viewport width.
❌ Incorrect: using sizes with pixel density descriptors
<img
src="photo-800.jpg"
srcset="photo-800.jpg 1x, photo-1600.jpg 2x"
sizes="(min-width: 600px) 800px, 100vw"
alt="A mountain landscape">
The sizes attribute and x descriptors cannot be combined. Either switch to w descriptors or remove sizes.
Quick reference
| Pattern | srcset descriptor | sizes required? |
|---|---|---|
| Viewport-based selection | Width (w) | Yes |
| Density-based selection | Pixel density (x) | No — must be omitted |
Remember that the w value in srcset refers to the image file's intrinsic pixel width (e.g., an 800-pixel-wide image gets 800w), while values in sizes use CSS length units like px, vw, or em to describe how wide the image will render in the layout.
According to the HTML specification, the width and height attributes on img elements accept only valid non-negative integers. A valid non-negative integer consists of one or more ASCII digits (0–9) with no other characters — no decimal points, no spaces, no units like px. When the validator encounters a value such as 602.88, it parses the digits 602 successfully, then hits the . character where it expects another digit or the end of the value, triggering the error.
This issue commonly arises when dimension values are generated programmatically — for example, when a CMS, image processing tool, or JavaScript calculation produces floating-point numbers and outputs them directly into the HTML. It can also happen when copying dimension values from CSS or design tools that work in sub-pixel units.
Why this matters
- Standards compliance: The HTML specification is explicit that these attributes take integer values. Using decimals produces invalid markup.
- Unpredictable rendering: Browsers may handle the malformed value in different ways — some might truncate at the decimal point, others might ignore the attribute entirely. This can lead to layout shifts or incorrectly sized images.
- Layout stability: The
widthandheightattributes are used by browsers to calculate the aspect ratio of an image before it loads, which helps prevent Cumulative Layout Shift (CLS). Invalid values can undermine this behavior, causing content to jump around as images load.
How to fix it
- Round to the nearest integer. If your value is
602.88, round it to603. If it's800.2, round to800. - Remove the decimal point entirely. The value must contain only digits.
- Do not include units. Values like
800pxare also invalid; use just800. - Fix the source of the values. If your CMS or build tool generates these attributes, update the logic to output integers (e.g., using
Math.round()in JavaScript orround()in PHP/Python).
Examples
❌ Incorrect: decimal values in width and height
<imgsrc="photo.jpg"alt="A golden retriever"height="602.88"width="800.2">
The validator reports errors for both attributes because . is not a valid character in a non-negative integer.
✅ Correct: whole number values
<imgsrc="photo.jpg"alt="A golden retriever"height="603"width="800">
Both values are valid non-negative integers with no decimal points.
❌ Incorrect: trailing decimal point with no fractional part
<imgsrc="banner.png"alt="Sale banner"width="1200."height="400.">
Even a trailing . with nothing after it is invalid — the parser still encounters an unexpected character.
✅ Correct: clean integer values
<imgsrc="banner.png"alt="Sale banner"width="1200"height="400">
Using CSS for sub-pixel precision
If you genuinely need sub-pixel sizing (which is rare for images), use CSS instead of HTML attributes. CSS width and height properties do accept decimal values:
<imgsrc="icon.svg"alt="Settings icon"style="width:24.5px;height:24.5px;">
However, keep in mind that you should still provide integer width and height HTML attributes for aspect ratio hinting, and then override with CSS if sub-pixel precision is needed:
<img
src="icon.svg"
alt="Settings icon"
width="25"
height="25"
style="width:24.5px;height:24.5px;">
This approach gives you valid HTML, proper aspect ratio hints for layout stability, and the precise sizing you need.
The HTML specification defines the width attribute on <img> elements as a "valid non-negative integer" — essentially a string of digits with no units, no decimals, and no percentage signs. When you write something like width="100%", the validator expects a digit character but encounters %, producing this error. While some older browsers historically accepted percentage values in the width attribute (a holdover from pre-HTML5 conventions), this was never part of the modern HTML standard and should not be relied upon.
This matters for several reasons. First, standards compliance ensures your markup behaves consistently across browsers and devices. Second, assistive technologies and browser layout engines may interpret an invalid width value unpredictably — some may ignore it entirely, others may parse it incorrectly. Third, the width and height attributes on <img> serve an important role in reserving layout space before the image loads (preventing Cumulative Layout Shift), but they only work correctly when set to valid integer pixel values that reflect the image's intrinsic or intended pixel dimensions.
How to fix it
If you want a fixed pixel width, provide just the integer without any unit:
<imgsrc="photo.jpg"alt="A red car"width="600">
If you need a percentage-based or responsive width, remove the width attribute from the HTML and use CSS instead. You can apply the style inline, via a <style> block, or in an external stylesheet.
If you want to preserve aspect ratio and prevent layout shift, keep the width and height attributes set to values that represent the image's intrinsic aspect ratio (in pixels), and then override the displayed size with CSS.
Examples
❌ Invalid: percentage in the width attribute
<imgsrc="photo.jpg"alt="A red car"width="100%">
This triggers the error because 100% is not a valid non-negative integer.
❌ Invalid: other non-integer values
<imgsrc="photo.jpg"alt="A red car"width="50%">
<imgsrc="banner.jpg"alt="Sale banner"width="300px">
<imgsrc="icon.png"alt="Settings icon"width="2.5">
Units like px, percentage signs, and decimal points are all invalid in the width attribute.
✅ Fixed: using a pixel integer
<imgsrc="photo.jpg"alt="A red car"width="800"height="600">
✅ Fixed: percentage width via inline CSS
<imgsrc="photo.jpg"alt="A red car"style="width:100%;">
✅ Fixed: responsive image with preserved aspect ratio
This approach sets the intrinsic dimensions in the attributes (to reserve layout space) and uses CSS to make the image responsive:
<style>
.responsive-img{
width:100%;
height: auto;
}
</style>
<imgsrc="photo.jpg"alt="A red car"width="800"height="600"class="responsive-img">
The browser uses the width and height attribute values to calculate the aspect ratio and reserve the correct amount of space, while CSS controls the actual rendered size. This is the recommended approach for responsive images because it avoids layout shift while still allowing flexible sizing.
In HTML, the width and height attributes on elements like <img> and <iframe> are defined as accepting only valid non-negative integers. According to the HTML specification, the value is implicitly in CSS pixels, so appending px or any other unit is both unnecessary and invalid. The parser expects every character in the value to be a digit (0–9), and when it encounters a letter like p, it reports the error.
This is a common mistake, especially for developers who frequently work with CSS, where px units are required. In HTML attributes, however, the convention is different — the pixel unit is implied, and adding it creates a malformed value. Browsers may still attempt to parse the number by ignoring the trailing characters, but this behavior is not guaranteed and should not be relied upon.
Getting these attributes right matters for several reasons:
- Standards compliance ensures your markup is predictable and portable across all browsers and user agents.
- Layout stability depends on the browser correctly reading
widthandheightto reserve space for images and iframes before they load, preventing cumulative layout shift (CLS). A malformed value could cause the browser to fall back to default sizing or ignore the attribute entirely. - Accessibility tools and screen readers may use these attributes to convey information about embedded content, and invalid values could interfere with that process.
If you need to set dimensions using units other than pixels (such as percentages or viewport units), use CSS instead of HTML attributes.
Examples
❌ Invalid: using px in the attribute value
<imgsrc="cat.jpg"alt="A cat sitting on a windowsill"width="225px"height="100px">
The validator reports an error because 225px and 100px contain the non-digit characters px.
✅ Valid: plain integers without units
<imgsrc="cat.jpg"alt="A cat sitting on a windowsill"width="225"height="100">
❌ Invalid: using percentage in the attribute value
<iframesrc="embed.html"width="100%"height="400px"title="Embedded content"></iframe>
Both 100% and 400px are invalid because they contain non-digit characters.
✅ Valid: plain integers on an <iframe>
<iframesrc="embed.html"width="800"height="400"title="Embedded content"></iframe>
✅ Using CSS when you need non-pixel units
If you need percentage-based or responsive sizing, apply it through CSS rather than HTML attributes:
<iframesrc="embed.html"style="width:100%;height:400px;"title="Embedded content"></iframe>
Or better yet, use an external stylesheet:
<style>
.responsive-frame{
width:100%;
height:400px;
}
</style>
<iframesrc="embed.html"class="responsive-frame"title="Embedded content"></iframe>
Quick reference of invalid vs. valid values
| Invalid value | Problem | Valid alternative |
|---|---|---|
225px | Contains px | 225 |
100% | Contains % | Use CSS instead |
20em | Contains em | Use CSS instead |
auto | Not a number | Use CSS instead |
10.5 | Decimal point | 10 or 11 |
The fix is straightforward: strip any unit suffixes from width and height HTML attributes and provide plain integer values. For anything beyond simple pixel dimensions, move your sizing logic to CSS.
Every <img> element must include at least a src or a srcset attribute to be valid HTML.
The <img> element exists to embed an image into the document, and it needs to know where that image is. The src attribute provides a single URL for the image, while srcset lets you offer multiple image sources for different screen sizes or resolutions.
You might run into this error when using JavaScript to set the image source dynamically, or when using lazy-loading libraries that store the URL in a data- attribute like data-src. While those techniques work at runtime, they produce invalid HTML because the validator still expects src or srcset to be present in the markup.
If you genuinely don't have a source yet, you can use a placeholder or a transparent pixel as the src value.
Invalid Example
<imgalt="A cute cat"loading="lazy"data-src="cat.jpg">
Valid Examples
Using src:
<imgsrc="cat.jpg"alt="A cute cat">
Using srcset:
<imgsrcset="cat-400.jpg 400w, cat-800.jpg 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A cute cat">
Using both src and a lazy-loading data-src (keeps the markup valid while still supporting lazy loading):
<imgsrc="placeholder.jpg"data-src="cat.jpg"alt="A cute cat">
The HTML specification mandates src on the <img> element because an image without a source has nothing to render. Omitting it produces invalid markup and unpredictable browser behavior — some browsers display a broken image icon, while others render nothing at all.
This issue commonly occurs in a few scenarios:
- Templating placeholders — A developer adds an
<img>tag intending to populate thesrcdynamically but forgets to set a default value. - Lazy loading implementations — Some lazy-loading scripts store the real URL in a
data-srcattribute and omitsrcentirely, which results in invalid HTML. - Incomplete markup — The attribute is simply forgotten during development.
How to fix it
- Add a
srcattribute with a valid URL pointing to your image. - If you're using lazy loading and want to defer the actual image source, set
srcto a lightweight placeholder (such as a tiny transparent image or a low-quality preview) and use the nativeloading="lazy"attribute instead of removingsrc.
Examples
❌ Missing src attribute
<imgalt="A sunset over the ocean">
This triggers the validation error because src is absent.
❌ Source stored only in a data- attribute
<imgdata-src="/images/photo.jpg"alt="A sunset over the ocean">
While data-src is a valid custom data attribute, it does not satisfy the requirement for src. The validator will still report the error.
✅ Correct usage with src
<imgsrc="/images/photo.jpg"alt="A sunset over the ocean">
✅ Lazy loading with a placeholder src
<img
src="/images/photo-placeholder.jpg"
data-src="/images/photo-full.jpg"
alt="A sunset over the ocean"
loading="lazy">
Here, src points to a small placeholder image so the markup stays valid, while data-src holds the full-resolution URL for your lazy-loading script to swap in.
✅ Native lazy loading (no JavaScript needed)
<imgsrc="/images/photo.jpg"alt="A sunset over the ocean"loading="lazy">
Modern browsers support the loading="lazy" attribute natively, so you can keep a valid src and still defer off-screen images without any custom scripting.
The <picture> element is a container that lets you provide multiple image sources for different viewport sizes, resolutions, or format support. It works together with zero or more <source> elements and exactly one <img> element. The browser evaluates each <source> in order, selects the best match based on its media, type, or srcset attributes, and then displays the chosen image in the space occupied by the <img> element. If no <source> matches—or if the browser doesn't support the <picture> element at all—the <img> element's src attribute is used as the final fallback.
The <img> element isn't optional; it's structurally required by the HTML specification. Without it, the <picture> element is incomplete and invalid. This matters for several reasons:
- Rendering: Browsers rely on the
<img>element to actually display the image. A<picture>element with only<source>children will render nothing in most browsers. - Accessibility: The
<img>element carries thealtattribute, which provides a text alternative for screen readers and other assistive technologies. Without it, there's no accessible description of the image. - Fallback support: Older browsers that don't understand
<picture>or<source>will ignore those elements entirely and fall back to the<img>. Without it, those users see nothing. - Standards compliance: The WHATWG HTML specification explicitly states that the
<picture>element's content model requires one<img>element, optionally preceded by<source>elements and inter-element whitespace.
The <img> element should always be the last child inside <picture>, placed after all <source> elements.
Examples
Invalid: <picture> without an <img> element
This markup triggers the validation error because the required <img> child is missing:
<picture>
<sourcesrcset="hero-large.webp"type="image/webp">
<sourcesrcset="hero-large.jpg"type="image/jpeg">
</picture>
Fixed: Adding the required <img> element
Add an <img> as the last child with a src pointing to the default image and a descriptive alt attribute:
<picture>
<sourcesrcset="hero-large.webp"type="image/webp">
<sourcesrcset="hero-large.jpg"type="image/jpeg">
<imgsrc="hero-large.jpg"alt="A panoramic view of the mountain landscape">
</picture>
Using <picture> for responsive images with media queries
When serving different image sizes based on viewport width, the <img> element provides the default (typically the smallest) image:
<picture>
<sourcesrcset="banner-wide.jpg"media="(min-width: 1200px)">
<sourcesrcset="banner-medium.jpg"media="(min-width: 600px)">
<imgsrc="banner-small.jpg"alt="Promotional banner for summer sale">
</picture>
The browser checks each <source> in order. If the viewport is 1200 pixels or wider, banner-wide.jpg is used. If it's between 600 and 1199 pixels, banner-medium.jpg is used. Otherwise, the <img> element's src value of banner-small.jpg is displayed.
Using <picture> for format selection
You can also use <picture> to offer modern image formats with a fallback for browsers that don't support them:
<picture>
<sourcesrcset="photo.avif"type="image/avif">
<sourcesrcset="photo.webp"type="image/webp">
<imgsrc="photo.jpg"alt="Close-up of a sunflower in bloom">
</picture>
Browsers that support AVIF will use the first source, those that support WebP will use the second, and all others will fall back to the JPEG specified in the <img> element. In every case, the <img> element is what makes the <picture> element valid and functional.
When the HTML parser encounters an <svg> or <math> tag, it switches from the HTML namespace into a foreign namespace (SVG or MathML, respectively). Inside these foreign contexts, only elements defined by those specifications are valid. The HTML <img> element does not exist in the SVG or MathML namespaces, so placing it there causes a parsing conflict and triggers the W3C validator error: "HTML start tag img in a foreign namespace context."
This matters for several reasons. Browsers handle this situation inconsistently — some may break out of the foreign namespace to render the <img>, while others may ignore it entirely or produce unexpected layout results. This inconsistency means your page may look correct in one browser but be broken in another. Beyond rendering issues, assistive technologies rely on proper namespace boundaries to interpret content. An <img> in the wrong namespace may lose its accessibility semantics, meaning the alt text could go unannounced to screen reader users.
How to fix it
The fix depends on which foreign namespace context contains the <img>:
Inside <svg>: Replace <img> with the SVG <image> element. Note that SVG's <image> uses the href attribute (not src) and requires x, y, width, and height attributes for positioning and sizing. Unfortunately, SVG's <image> element does not support an alt attribute directly — use a <title> child element or aria-label to provide an accessible name.
Inside <math>: Move the <img> outside the <math> element entirely, or place it inside an <mtext> element. The <mtext> element in MathML is designed to hold text content and, per the HTML parsing rules, can contain embedded HTML elements.
Examples
Incorrect: <img> inside <svg>
<svgwidth="200"height="200">
<imgsrc="diagram.png"alt="A diagram"width="200"height="200">
</svg>
Correct: using SVG <image> element
<svgwidth="200"height="200"role="img"aria-label="A diagram">
<imagehref="diagram.png"x="0"y="0"width="200"height="200"/>
</svg>
Correct: with an accessible name via <title>
<svgwidth="200"height="200"role="img"aria-labelledby="diagram-title">
<titleid="diagram-title">A diagram</title>
<imagehref="diagram.png"x="0"y="0"width="200"height="200"/>
</svg>
Incorrect: <img> inside <math>
<math>
<imgsrc="equation.png"alt="x squared plus one">
</math>
Correct: <img> inside <mtext> within <math>
<math>
<mtext>
<imgsrc="equation.png"alt="x squared plus one">
</mtext>
</math>
Correct: <img> moved outside <math>
<imgsrc="equation.png"alt="x squared plus one">
Incorrect: mixed HTML content inside <svg>
<svgwidth="300"height="150">
<rectwidth="300"height="150"fill="#eee"/>
<imgsrc="icon.png"alt="Icon"width="32"height="32">
<textx="50"y="80">Hello</text>
</svg>
Correct: using <image> alongside other SVG elements
<svgwidth="300"height="150">
<rectwidth="300"height="150"fill="#eee"/>
<imagehref="icon.png"x="10"y="10"width="32"height="32"aria-label="Icon"/>
<textx="50"y="80">Hello</text>
</svg>
If you need to place HTML content alongside or on top of an SVG, consider using the <foreignObject> SVG element, which explicitly creates an HTML namespace context within SVG:
<svgwidth="300"height="150">
<rectwidth="300"height="150"fill="#eee"/>
<foreignObjectx="10"y="10"width="100"height="100">
<imgsrc="icon.png"alt="Icon"width="32"height="32">
</foreignObject>
</svg>
The <foreignObject> approach is especially useful when you need full HTML capabilities — such as alt text on <img>, form controls, or rich text — inside an SVG graphic.
In earlier versions of HTML, the border attribute on <img> was commonly used to control the border width in pixels. Its most frequent use was border="0" to suppress the default blue border browsers would render around images wrapped in <a> links. While this worked, it mixed presentation with markup — something HTML5 discourages in favor of a clean separation between structure (HTML) and styling (CSS).
The W3C HTML Validator flags this attribute as obsolete because it was removed from the HTML5 specification. Modern browsers still understand it for backward compatibility, but relying on deprecated features is bad practice. It can lead to inconsistencies across browsers, makes your code harder to maintain, and signals to validators and other developers that the markup is outdated.
The recommended approach is to handle image borders entirely in CSS. If you previously used border="0" to remove borders from linked images, most modern CSS resets and normalize stylesheets already handle this. If you're not using a reset, a single CSS rule takes care of it globally — no need to repeat the attribute on every <img> tag.
How to fix it
- Remove the
borderattribute from all<img>elements. - Add a CSS rule to achieve the same effect. For removing borders, use
img { border: 0; }in your stylesheet. For adding a visible border, use properties likeborder: 2px solid #333;.
Examples
❌ Obsolete border attribute
<ahref="/products">
<imgsrc="product.jpg"alt="Our product"border="0">
</a>
This triggers the validator warning because border is no longer a valid attribute on <img>.
✅ Fixed with CSS (external/internal stylesheet)
<style>
img{
border:0;
}
</style>
<ahref="/products">
<imgsrc="product.jpg"alt="Our product">
</a>
A single rule in your stylesheet removes borders from all images, which is cleaner and easier to maintain than repeating the attribute on every element.
✅ Fixed with inline CSS (if needed)
<ahref="/products">
<imgsrc="product.jpg"alt="Our product"style="border:0;">
</a>
Inline styles work but aren't ideal for large-scale fixes. Prefer a stylesheet rule when possible.
✅ Adding a decorative border with CSS
If your intent was to add a visible border (e.g., border="2"), replace it with a more flexible CSS equivalent:
<style>
.bordered{
border:2px solid #333;
}
</style>
<imgsrc="photo.jpg"alt="A scenic landscape"class="bordered">
CSS gives you far more control — you can specify the border style, color, individual sides, and even use border-radius for rounded corners, none of which were possible with the old border attribute.
The border attribute on the <img> element is obsolete in HTML5 and should be replaced with CSS.
Older versions of HTML allowed border as an attribute directly on <img> elements, most commonly set to 0 to remove the blue border that browsers added around images wrapped in links. HTML5 dropped this attribute from the specification. Modern browsers no longer add that default border, so in most cases the attribute can simply be removed.
If you still need to control the border on an image, use CSS instead. You can apply a style directly with the style attribute, use a class, or add a rule in your stylesheet.
HTML examples
Invalid: using the obsolete border attribute
<ahref="/home">
<imgsrc="logo.png"alt="Logo"border="0">
</a>
Valid: using CSS instead
<ahref="/home">
<imgsrc="logo.png"alt="Logo"style="border:0;">
</a>
Or, with a stylesheet rule:
<style>
img{
border:0;
}
</style>
<ahref="/home">
<imgsrc="logo.png"alt="Logo">
</a>
The border attribute on the <table> element is obsolete in HTML5 and should be replaced with CSS styling.
The border attribute was commonly used in older HTML to add borders to tables. In HTML5, the only valid value for the border attribute on a <table> is an empty string ("") or "1", and even then it's considered obsolete. The validator message mentions img { border: 0; } because the border attribute was also frequently used on <img> elements, but the same principle applies to tables — presentational attributes should be replaced with CSS.
To style table borders, use the CSS border property on the <table>, <th>, and <td> elements. The border-collapse property is also useful for controlling whether borders are merged or separated.
HTML Examples
❌ Invalid: using the obsolete border attribute
<tableborder="2">
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
</tr>
</table>
✅ Valid: using CSS for borders
<style>
table,td{
border:2px solid black;
border-collapse: collapse;
}
</style>
<table>
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
</tr>
</table>
Every HTML element has an implicit ARIA role defined by the HTML specification. The <img> element's implicit role is img, which means assistive technologies like screen readers already recognize it as an image without any additional ARIA attributes. Adding role="img" explicitly doesn't change behavior — it just adds unnecessary noise to your markup and signals that the author may not understand how native semantics work.
The W3C validator flags this because it violates the first rule of ARIA: don't use ARIA if you can use a native HTML element or attribute that already has the semantics you need. Redundant roles clutter your code, make maintenance harder, and can confuse other developers into thinking the role is there for a specific reason.
The role="img" attribute is genuinely useful in other contexts — for example, when you want to group multiple elements together and have them treated as a single image by assistive technologies. A <div> or <span> has no implicit img role, so adding role="img" to a container is meaningful and appropriate.
How to fix it
Simply remove the role="img" attribute from any <img> element. The image semantics are already built in. Make sure you still provide a meaningful alt attribute for accessibility.
Examples
❌ Redundant role on <img>
<imgsrc="photo.jpg"alt="A sunset over the ocean"role="img">
The validator will warn: The "img" role is unnecessary for element "img".
✅ Fixed: Remove the redundant role
<imgsrc="photo.jpg"alt="A sunset over the ocean">
No explicit role is needed. The browser already communicates this element as an image.
✅ Legitimate use of role="img" on a non-image element
The role="img" attribute is appropriate when applied to a container that groups multiple elements into a single conceptual image:
<divrole="img"aria-label="Star rating: 4 out of 5">
<span>⭐</span>
<span>⭐</span>
<span>⭐</span>
<span>⭐</span>
<span>☆</span>
</div>
Here, the <div> has no inherent image semantics, so role="img" is meaningful — it tells assistive technologies to treat the entire group as a single image described by the aria-label.
✅ Another legitimate use: CSS background image with role="img"
<divrole="img"aria-label="Company logo"class="logo-background"></div>
Since a <div> styled with a CSS background image has no image semantics, role="img" paired with aria-label ensures the visual content is accessible.
The longdesc attribute dates back to HTML4, where it accepted a URL pointing to a separate page (or section of a page) containing a detailed description of the image. The idea was to supplement the short text in the alt attribute with a more comprehensive explanation, particularly useful for complex images like charts, diagrams, or infographics.
HTML5 made longdesc obsolete for several reasons. Browser support was inconsistent — most browsers never exposed the attribute in a way that was easily discoverable by users. Many developers misused it by placing literal descriptions in the attribute instead of URLs, or left it pointing to broken links. Because the attribute was invisible in the rendered page, there was no visual indication that a longer description existed, making it practically useless for sighted users and unreliable for assistive technology users.
The recommended replacements are more robust and accessible:
- Wrap the image in an
aelement (or place a link nearby) that points to the description page. This makes the link visible and usable by everyone. - Use
aria-describedbyto reference a description that already exists on the same page. This is ideal when the detailed description is displayed alongside the image. - Use a
figurewithfigcaptionto associate a visible caption or description directly with the image.
These approaches are better for accessibility because they work reliably across browsers and assistive technologies, and they make the description discoverable to all users, not just those using specific screen readers that happened to support longdesc.
Examples
❌ Obsolete: using longdesc
<img
src="cat.jpg"
alt="Smiling cat sitting on a windowsill"
longdesc="descriptions/smiling-cat.html">
This triggers the validation error because longdesc is no longer a valid attribute on img in HTML5.
✅ Fix: wrap the image in a link
The simplest replacement is to make the image itself a link to the description:
<ahref="descriptions/smiling-cat.html">
<imgsrc="cat.jpg"alt="Smiling cat sitting on a windowsill">
</a>
✅ Fix: provide a separate link near the image
If you don't want the image itself to be clickable, place a visible link nearby:
<figure>
<imgsrc="chart.png"alt="Quarterly revenue chart for 2024">
<figcaption>
Quarterly revenue chart.
<ahref="descriptions/revenue-chart.html">View detailed description</a>
</figcaption>
</figure>
✅ Fix: use aria-describedby for on-page descriptions
When the long description is already on the same page, reference it with aria-describedby:
<figure>
<img
src="chart.png"
alt="Quarterly revenue chart for 2024"
aria-describedby="chart-description">
<figcaptionid="chart-description">
Revenue grew from $2.1M in Q1 to $3.8M in Q4, with the largest
quarter-over-quarter increase occurring between Q2 and Q3.
</figcaption>
</figure>
This approach keeps the description visible on the page and programmatically associates it with the image for screen readers.
Choosing the right approach
| Scenario | Recommended approach |
|---|---|
| Description is on a separate page | Wrap image in an a element or add a nearby link |
| Description is visible on the same page | Use aria-describedby pointing to the description's id |
| Image needs a brief visible caption | Use figure with figcaption |
| Complex image (chart, diagram, infographic) | Combine figure, figcaption, and a link to a full description |
In all cases, make sure the alt attribute still provides a meaningful short description. The long description supplements alt — it doesn't replace it.
The name attribute was historically used on img elements to reference images through JavaScript's document.images collection or via document.getElementsByName(). In early HTML, name served as an identifier for various elements before the id attribute was widely adopted. The HTML living standard (WHATWG) now marks name as obsolete on img elements, meaning it should no longer be used in new content.
This matters for several reasons:
- Standards compliance: Using obsolete attributes means your HTML does not conform to the current specification, which can cause validation errors and may lead to unexpected behavior in future browser versions.
- Consistency: The
idattribute is the universal mechanism for uniquely identifying any HTML element. Usingidinstead ofnamekeeps your markup consistent and predictable. - JavaScript and CSS targeting: Modern APIs like
document.getElementById()anddocument.querySelector()work withid, notnameon image elements. CSS selectors also target elements byid(e.g.,#myImage), makingidthe more versatile choice. - Fragment linking: The
idattribute allows you to link directly to an element using a URL fragment (e.g.,page.html#myImage), whereas the obsoletenameattribute onimgdoes not serve this purpose.
To fix this issue, simply replace name with id on your img elements. Keep in mind that id values must be unique within the entire document — no two elements can share the same id. If you have JavaScript code that references the image by name (e.g., document.images["myImage"] or document.getElementsByName("myImage")), update those references to use document.getElementById("myImage") or document.querySelector("#myImage") instead.
Examples
Incorrect: using the obsolete name attribute
<imgsrc="photo.jpg"name="heroImage"alt="A sunset over the ocean">
This triggers the validation error because name is no longer a valid attribute on img.
Correct: using the id attribute
<imgsrc="photo.jpg"id="heroImage"alt="A sunset over the ocean">
The name attribute is replaced with id, and the element can now be targeted with document.getElementById("heroImage") or the CSS selector #heroImage.
Updating JavaScript references
If your existing code references the image by name, update it accordingly.
Before (relying on name):
<imgsrc="logo.png"name="siteLogo"alt="Company logo">
<script>
varlogo=document.images["siteLogo"];
logo.style.border="2px solid blue";
</script>
After (using id):
<imgsrc="logo.png"id="siteLogo"alt="Company logo">
<script>
varlogo=document.getElementById("siteLogo");
logo.style.border="2px solid blue";
</script>
Multiple images that previously shared a name
Since id values must be unique, you cannot give the same id to multiple elements. If you previously used the same name on several images and selected them as a group, switch to a shared class instead:
<imgsrc="slide1.jpg"class="gallery-image"alt="Mountain landscape">
<imgsrc="slide2.jpg"class="gallery-image"alt="Forest trail">
<imgsrc="slide3.jpg"class="gallery-image"alt="River valley">
<script>
varimages=document.querySelectorAll(".gallery-image");
images.forEach(function(img){
img.style.borderRadius="8px";
});
</script>
This approach is standards-compliant and gives you flexible, modern element selection using class for groups and id for unique elements.
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
Pro Trial
Full Pro access. Cancel anytime.
Start Pro Trial →Join teams across 40+ countries