HTML Guides
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 lang attribute on the <html> element declares the primary language of the document's content. When this attribute is left empty (lang=""), it effectively tells browsers and assistive technologies that the language is unknown or intentionally unspecified — which is almost never what you want.
This matters for several important reasons:
- Accessibility: Screen readers rely on the
langattribute to select the correct pronunciation engine. An empty value can cause a screen reader to fall back to a default language, potentially reading English text with incorrect pronunciation rules. - Search engines: Search engines use the
langattribute to understand what language your content is in, which helps serve your pages to the right audience. - Browser features: Browsers use the language declaration for hyphenation, spell-checking, font selection, and other language-sensitive rendering decisions.
- Standards compliance: The WHATWG HTML living standard specifies that if the
langattribute is present, its value must be a valid BCP 47 language tag. An empty string is not a valid language tag.
The fix is straightforward: set the lang attribute to a valid BCP 47 language tag that matches your content. For English, common values include en (general English), en-US (American English), or en-GB (British English). If your content is in another language, use the appropriate tag (e.g., fr for French, de for German, ja for Japanese).
Examples
❌ Empty lang attribute (triggers the warning)
<!DOCTYPE html>
<html lang="">
<head>
<title>My Page</title>
</head>
<body>
<h1>Welcome to my website</h1>
</body>
</html>
❌ Missing lang attribute entirely
While a missing lang attribute triggers a different warning, it causes the same underlying problem — no language is declared:
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
</head>
<body>
<h1>Welcome to my website</h1>
</body>
</html>
✅ Correct: specifying the language
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
</head>
<body>
<h1>Welcome to my website</h1>
</body>
</html>
✅ Correct: using a regional variant
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>My Page</title>
</head>
<body>
<h1>Welcome to my website</h1>
</body>
</html>
Using lang for mixed-language content
If your document is primarily in English but contains sections in other languages, set lang="en" on the <html> element and override it on specific elements:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Multilingual Page</title>
</head>
<body>
<h1>Welcome</h1>
<p>This page contains a quote in French:</p>
<blockquote lang="fr">
<p>La vie est belle.</p>
</blockquote>
</body>
</html>
A sizes value that starts with auto lets the browser work out an image's display size on its own, but the specification only permits it when the same <img> is lazy-loaded with loading="lazy".
The auto keyword removes the need to hand-write media-condition source sizes for a responsive image. For the browser to measure the rendered width and pick a candidate from srcset, the layout around the image has to already exist when the image is fetched. That holds for a lazy-loaded image, which is requested only as it approaches the viewport. An eagerly loaded image can be fetched before its box is laid out, so auto would have nothing to measure, and the validator rejects it.
To fix the error, add loading="lazy" to the same <img>. If the image has to load eagerly, such as a hero image at the top of the page, drop the auto keyword and give sizes explicit lengths instead.
Invalid example
<img
srcset="small.jpg 480w, large.jpg 1024w"
sizes="auto"
src="large.jpg"
alt="Product photo">
Valid example
<img
srcset="small.jpg 480w, large.jpg 1024w"
sizes="auto"
loading="lazy"
src="large.jpg"
alt="Product photo">
The <picture> element exists to give browsers a choice between multiple image sources. The browser evaluates each <source> in order, looking for the first one whose conditions match the current environment. These conditions are expressed through the media attribute (e.g., viewport width or resolution) and the type attribute (e.g., image/webp or image/avif). If a <source> lacks both attributes, it acts as an unconditional match — the browser will always select it, making any subsequent <source> elements or an <img> with srcset unreachable. This defeats the purpose of the <picture> element entirely.
The HTML specification requires these attributes specifically to prevent this situation. When a <source> has a following sibling <source> or a following <img> with srcset, at least one selection criterion (media or type) must be present so the browser can meaningfully choose between the options. A <source> without these attributes is only valid if it's the last <source> before a plain <img> (one without srcset), since in that case it serves as the final fallback within the <picture>.
This matters for several reasons:
- Standards compliance: The HTML living standard explicitly defines this requirement. Violating it produces a validation error.
- Predictable rendering: Without distinguishing attributes, browsers may silently ignore sources or always pick the first one, leading to inconsistent behavior across browsers.
- Performance: The
<picture>element is often used to serve smaller images on small viewports or modern formats like WebP and AVIF to browsers that support them. Without propermediaortypeattributes, these optimizations won't work as intended.
How to fix it
Add a media attribute, a type attribute, or both to each <source> element that is followed by another <source> or an <img> with srcset:
- Use
typewhen you're offering the same image in different formats (e.g., AVIF, WebP, JPEG). The browser picks the first format it supports. - Use
mediawhen you're serving different images based on viewport conditions (art direction). The browser picks the source whose media query matches. - Use both when you want to combine format negotiation with art direction.
Examples
Incorrect — <source> without media or type
Each <source> below has no selection criterion, so the browser has no way to choose between them:
<picture>
<source srcset="hero.webp">
<source srcset="hero.jpg">
<img src="hero.jpg" srcset="hero-2x.jpg 2x" alt="A mountain landscape">
</picture>
Correct — using type for format negotiation
Adding type lets the browser pick the first format it supports:
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" srcset="hero-2x.jpg 2x" alt="A mountain landscape">
</picture>
Correct — using media for art direction
Adding media lets the browser pick the source that matches the viewport:
<picture>
<source srcset="hero-wide.jpg" media="(min-width: 1024px)">
<source srcset="hero-narrow.jpg" media="(max-width: 1023px)">
<img src="hero-narrow.jpg" srcset="hero-narrow-2x.jpg 2x" alt="A mountain landscape">
</picture>
Correct — combining media and type
You can use both attributes together to serve the right format at the right viewport size:
<picture>
<source srcset="hero-wide.avif" media="(min-width: 1024px)" type="image/avif">
<source srcset="hero-wide.webp" media="(min-width: 1024px)" type="image/webp">
<source srcset="hero-narrow.avif" media="(max-width: 1023px)" type="image/avif">
<source srcset="hero-narrow.webp" media="(max-width: 1023px)" type="image/webp">
<img src="hero-narrow.jpg" alt="A mountain landscape">
</picture>
Correct — single <source> before a plain <img>
When there's only one <source> and the <img> has no srcset, no media or type is required — but adding type is still recommended for clarity:
<picture>
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="A mountain landscape">
</picture>
The sandbox attribute applies a strict set of restrictions to content loaded inside an <iframe>. By default, sandboxed iframes cannot run scripts, submit forms, open popups, or access storage. You can selectively lift specific restrictions by adding recognized keywords like allow-scripts, allow-forms, allow-popups, allow-same-origin, and others defined in the WHATWG HTML standard.
The keyword allow-storage-access-by-user-activation was proposed as a way to let sandboxed iframes request access to first-party storage (such as cookies) after a user gesture. However, this keyword was never adopted into the HTML specification. The functionality it aimed to provide is now handled by the Storage Access API, which uses document.requestStorageAccess() and document.hasStorageAccess() in JavaScript. Because the keyword was never standardized, the W3C validator correctly flags it as invalid.
Why this matters
- Standards compliance: Using non-standard keywords means your HTML doesn't conform to the specification, which the validator will flag as an error.
- Browser inconsistency: Since this keyword was experimental and never standardized, browser support is unreliable. Some browsers may silently ignore it, while others may have briefly supported it before removing it.
- False sense of security: Including an unrecognized sandbox keyword doesn't actually enable the behavior you expect. The iframe won't gain storage access just because this keyword is present—the browser simply ignores unknown tokens.
How to fix it
- Remove the invalid keyword from the
sandboxattribute. - Keep any other valid sandbox keywords that your iframe needs.
- Use the Storage Access API in JavaScript within the iframe if you need cross-site storage access. The embedded page must call
document.requestStorageAccess()in response to a user gesture, and thesandboxattribute must includeallow-scriptsandallow-same-originfor this API to work.
Examples
❌ Invalid: using a non-standard sandbox keyword
<iframe
src="https://example.com/widget"
sandbox="allow-scripts allow-same-origin allow-storage-access-by-user-activation">
</iframe>
✅ Valid: removing the non-standard keyword
<iframe
src="https://example.com/widget"
sandbox="allow-scripts allow-same-origin">
</iframe>
The embedded page at https://example.com/widget can then use the Storage Access API in JavaScript:
document.querySelector('#login-button').addEventListener('click', async () => {
const hasAccess = await document.hasStorageAccess();
if (!hasAccess) {
await document.requestStorageAccess();
}
// Storage (cookies, etc.) is now accessible
});
✅ Valid: sandbox with other standard keywords
If your iframe doesn't need storage access at all, simply use the standard keywords you require:
<iframe
src="https://example.com/form"
sandbox="allow-scripts allow-forms allow-popups">
</iframe>
Note that for document.requestStorageAccess() to work inside a sandboxed iframe, you must include both allow-scripts (so JavaScript can run) and allow-same-origin (so the iframe retains its origin). Without these, the Storage Access API calls will fail.
When an img element has a sizes attribute, every image candidate in the srcset attribute must include a width descriptor (like 480w), not a pixel density descriptor (like 2x) or a bare URL.
The srcset attribute accepts two types of descriptors, but they cannot be mixed, and the choice depends on whether sizes is present.
Width descriptors (w) tell the browser the actual pixel width of each source image. The browser then uses the sizes attribute to determine how much space the image will occupy in the layout and picks the best candidate from srcset. This pairing of srcset with width descriptors and sizes is how responsive image selection works.
Pixel density descriptors (x) tell the browser which image to use based on the device's pixel density (e.g., 1x for standard screens, 2x for retina). When using density descriptors, the sizes attribute has no role and should be omitted.
The validation error appears when sizes is present but one or more entries in srcset lack a w descriptor. A bare URL with no descriptor is treated as 1x by default, which conflicts with the requirement that sizes demands width descriptors.
Invalid example
<img
src="photo-800.jpg"
srcset="photo-400.jpg, photo-800.jpg 2x"
sizes="(max-width: 600px) 100vw, 50vw"
alt="A landscape photo">
Here, photo-400.jpg has no descriptor (defaults to 1x), and photo-800.jpg uses 2x. Because sizes is present, the validator expects every candidate to specify a width.
Fixed example
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="A landscape photo">
Each candidate now includes a width descriptor that reflects the image's intrinsic width in pixels. The browser compares these widths against the resolved sizes value to choose the most appropriate source.
If you do not need sizes and just want to serve different images for different pixel densities, drop the sizes attribute and use density descriptors instead:
<img
src="photo-400.jpg"
srcset="photo-400.jpg 1x, photo-800.jpg 2x"
alt="A landscape photo">
The srcset attribute supports two types of descriptors: width descriptors (like 480w) and pixel density descriptors (like 2x). However, these two types cannot be mixed, and the sizes attribute is only compatible with width descriptors. The sizes attribute tells the browser how wide the image will be displayed at various viewport sizes, and the browser uses this information along with the width descriptors in srcset to choose the most appropriate image file. If sizes is present but an image candidate lacks a width descriptor, the browser cannot perform this calculation correctly.
This matters for several reasons. First, it violates the WHATWG HTML specification, which explicitly requires that when sizes is present, all image candidates must use width descriptors. Second, browsers may ignore malformed srcset values or fall back to unexpected behavior, resulting in the wrong image being loaded — potentially hurting performance by downloading unnecessarily large files or degrading visual quality by selecting a too-small image. Third, standards-compliant markup ensures consistent, predictable behavior across all browsers and devices.
A common mistake is specifying a plain URL without any descriptor, or mixing density descriptors (1x, 2x) with the sizes attribute. An image candidate string without any descriptor defaults to 1x, which is a density descriptor — and that conflicts with the presence of sizes.
Examples
❌ Incorrect: Missing width descriptor with sizes present
<picture>
<source
srcset="image-small.jpg, image-large.jpg 1024w"
sizes="(max-width: 600px) 480px, 800px">
<img src="image-fallback.jpg" alt="A scenic landscape">
</picture>
Here, image-small.jpg has no width descriptor. Since sizes is present, this triggers the validation error.
❌ Incorrect: Using density descriptors with sizes
<img
srcset="image-1x.jpg 1x, image-2x.jpg 2x"
sizes="(max-width: 600px) 480px, 800px"
src="image-fallback.jpg"
alt="A scenic landscape">
Density descriptors (1x, 2x) are incompatible with the sizes attribute.
✅ Correct: All candidates have width descriptors
<picture>
<source
srcset="image-small.jpg 480w, image-large.jpg 1024w"
sizes="(max-width: 600px) 480px, 800px">
<img src="image-fallback.jpg" alt="A scenic landscape">
</picture>
Every image candidate now includes a width descriptor, which pairs correctly with the sizes attribute.
✅ Correct: Using density descriptors without sizes
If you want to use density descriptors instead of width descriptors, simply remove the sizes attribute:
<img
srcset="image-1x.jpg 1x, image-2x.jpg 2x"
src="image-fallback.jpg"
alt="A scenic landscape">
This is valid because density descriptors don't require (and shouldn't be used with) the sizes attribute.
✅ Correct: Width descriptors on <img> with sizes
<img
srcset="photo-320.jpg 320w, photo-640.jpg 640w, photo-1280.jpg 1280w"
sizes="(max-width: 400px) 320px, (max-width: 800px) 640px, 1280px"
src="photo-640.jpg"
alt="A close-up of a flower">
Each entry in srcset specifies its intrinsic width, and sizes tells the browser which display width to expect at each breakpoint. The browser then selects the best-fitting image automatically.
A sizes attribute on an <img> element contains a media condition where a number is missing its CSS unit (like px, em, or rem).
The sizes attribute tells the browser how wide an image will be displayed at different viewport sizes, so it can pick the best source from a srcset. Each entry in sizes is a media condition paired with a length value. The media conditions follow standard CSS media query syntax, which means all non-zero numeric values must include a unit.
A common mistake is writing something like (max-width: 600) instead of (max-width: 600px). In CSS, bare numbers without units are invalid except for 0, which doesn't need a unit because zero pixels, zero ems, and zero rems are all the same thing.
Example with the error
<img
src="photo.jpg"
srcset="photo-480.jpg 480w, photo-800.jpg 800w"
sizes="(max-width: 600) 480px, 800px"
alt="A sunset over the ocean">
The media condition (max-width: 600) is missing a unit after 600.
Fixed example
<img
src="photo.jpg"
srcset="photo-480.jpg 480w, photo-800.jpg 800w"
sizes="(max-width: 600px) 480px, 800px"
alt="A sunset over the ocean">
Adding px (or whichever unit is appropriate) to the number in the media condition resolves the error. This applies to every numeric value in the media condition, not just the length that follows it. For example, (min-width: 40em) 50vw, 100vw is also valid.
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.
The HTML specification enforces this rule because a required <select> element needs options for the user to choose from. Without any <option> children, the element is semantically meaningless — it's a dropdown with nothing to select, yet the form demands a selection before submission. This creates an impossible situation for the user and an ambiguous state for the browser.
This rule specifically applies when all three of these conditions are true:
- The
requiredattribute is present. - The
multipleattribute is not present. - The
sizeattribute is either absent or set to1(the default for a single-selection<select>).
When multiple is set or size is greater than 1, the <select> behaves differently (as a list box rather than a dropdown), and the specification relaxes this constraint. But for the standard single-selection dropdown, at least one <option> is mandatory.
Why this matters
- Usability: A required dropdown with no options gives users no way to satisfy the form requirement, effectively blocking form submission entirely.
- Accessibility: Screen readers announce
<select>elements along with their available options. An empty required dropdown creates a confusing experience for assistive technology users. - Form validation: Browsers use the first
<option>with an emptyvalueas the placeholder. The built-in constraint validation forrequiredselects relies on checking whether the selected option's value is a non-empty string. Without any options, this validation behavior is undefined.
How the placeholder pattern works
The HTML specification defines a specific behavior for required single-selection dropdowns: the first <option> element, if it has an empty value attribute, acts as a placeholder. When the form is submitted with this placeholder still selected, browser validation will reject the submission because the value is empty. This is the standard pattern for prompting users to make a deliberate choice.
Examples
❌ Invalid: required <select> with no options
<label for="color">Pick a color:</label>
<select id="color" name="color" required>
</select>
This triggers the validation error because there are no <option> elements inside the required <select>.
❌ Invalid: required <select> with only a group but no options
<label for="color">Pick a color:</label>
<select id="color" name="color" required>
<optgroup label="Colors">
</optgroup>
</select>
An empty <optgroup> doesn't satisfy the requirement. The <select> still needs at least one <option>.
✅ Valid: required <select> with a placeholder and options
<label for="color">Pick a color:</label>
<select id="color" name="color" required>
<option value="">--Select a color--</option>
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
The first <option> has an empty value, so it serves as a placeholder. The browser will require the user to choose one of the other options before submitting the form.
✅ Valid: required <select> with multiple (rule does not apply)
<label for="colors">Pick one or more colors:</label>
<select id="colors" name="colors" required multiple>
</select>
This does not trigger the error because the multiple attribute is present, which exempts the element from this particular rule. However, an empty multi-select is still poor UX and should generally be avoided.
✅ Valid: required <select> with size greater than 1 (rule does not apply)
<label for="colors">Pick a color:</label>
<select id="colors" name="colors" required size="4">
</select>
When size is greater than 1, the element renders as a list box and the rule no longer applies. Again, while technically valid, an empty list box isn't useful in practice.
✅ Valid: required <select> with grouped options
<label for="vehicle">Choose a vehicle:</label>
<select id="vehicle" name="vehicle" required>
<option value="">--Select a vehicle--</option>
<optgroup label="Cars">
<option value="sedan">Sedan</option>
<option value="suv">SUV</option>
</optgroup>
<optgroup label="Trucks">
<option value="pickup">Pickup</option>
<option value="semi">Semi</option>
</optgroup>
</select>
Options inside <optgroup> elements count as child options of the <select>, so this is fully valid.
The srcset attribute supports two types of descriptors: width descriptors (e.g., 480w) and pixel density descriptors (e.g., 2x). These two types cannot be mixed, and the sizes attribute is specifically designed to work with width descriptors. The sizes attribute tells the browser how wide the image will be displayed at various viewport sizes, so the browser can then pick the best image from srcset based on the widths you've provided. If any candidate in srcset lacks a width descriptor — or uses a density descriptor instead — the browser can't perform this calculation, and the HTML is invalid.
This matters for several reasons. First, browsers rely on the combination of sizes and width descriptors to make intelligent decisions about which image to download before the layout is computed. An invalid srcset can lead to the browser ignoring the entire attribute or selecting a suboptimal image, wasting bandwidth or displaying a blurry result. Second, standards compliance ensures consistent behavior across all browsers and devices.
A common mistake is specifying sizes while using density descriptors (1x, 2x) or providing bare URLs without any descriptor in srcset. If you want to use density descriptors, simply remove the sizes attribute. If you want responsive image selection based on viewport width, use width descriptors for every candidate.
Examples
Incorrect: Using density descriptors with sizes
<picture>
<source
srcset="image-small.jpg 1x, image-large.jpg 2x"
sizes="(max-width: 600px) 100vw, 50vw">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
This triggers the error because 1x and 2x are density descriptors, but the sizes attribute requires width descriptors.
Incorrect: Missing descriptor on one candidate
<picture>
<source
srcset="image-small.jpg, image-large.jpg 800w"
sizes="(max-width: 600px) 100vw, 50vw">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
Here, image-small.jpg has no descriptor at all. When sizes is present, every candidate must have a width descriptor.
Correct: All candidates use width descriptors with sizes
<picture>
<source
srcset="image-small.jpg 400w, image-large.jpg 800w"
sizes="(max-width: 600px) 100vw, 50vw">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
Each image candidate now specifies a width descriptor (400w, 800w), which matches the requirement imposed by the sizes attribute.
Correct: Using density descriptors without sizes
If you only need density-based selection (e.g., for retina displays) and don't need viewport-based sizing, remove the sizes attribute entirely:
<picture>
<source srcset="image-small.jpg 1x, image-large.jpg 2x">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
Correct: Using srcset with width descriptors on <img>
The same rules apply when using srcset directly on an <img> element:
<img
srcset="photo-320.jpg 320w, photo-640.jpg 640w, photo-1024.jpg 1024w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 33vw"
src="photo-640.jpg"
alt="A mountain landscape">
Every candidate in srcset includes a width descriptor, making this fully valid alongside the sizes attribute. The src attribute serves as the fallback for browsers that don't support srcset.
The srcset attribute allows you to provide multiple image sources so the browser can choose the most appropriate one based on the user's device and viewport. There are two distinct modes for srcset:
- Width descriptor mode — Each candidate specifies the image's intrinsic width using a
wdescriptor (e.g.,640w). This mode requires thesizesattribute so the browser knows how much space the image will occupy in the layout, enabling it to pick the best candidate. - Density descriptor mode — Each candidate specifies a pixel density using an
xdescriptor (e.g.,2x). This mode does not use thesizesattribute; the browser simply matches candidates to the device's pixel density.
These two modes are mutually exclusive. You cannot mix w and x descriptors in the same srcset, and you cannot use x descriptors (or bare URLs with no descriptor) when sizes is present. The HTML specification is explicit about this: if sizes is specified, all image candidate strings must include a width descriptor.
Why this matters
- Standards compliance: The WHATWG HTML Living Standard defines strict parsing rules for
srcset. Whensizesis present, the browser's source selection algorithm expects width descriptors. Providing density descriptors or bare candidates in this context violates the spec and produces undefined behavior. - Broken image selection: Browsers rely on the
sizesattribute to calculate whichw-described image best fits the current layout width. If you providexdescriptors alongsidesizes, the browser may ignore thesrcsetentirely or fall back to thesrcattribute, defeating the purpose of responsive images. - Accessibility and performance: Responsive images exist to serve appropriately sized files to different devices. An invalid
srcset/sizescombination can result in oversized images being downloaded on small screens (wasting bandwidth) or undersized images on large screens (reducing visual quality).
How to fix it
You have two options:
- Keep
sizesand switch to width descriptors — Replace everyxdescriptor (or missing descriptor) insrcsetwith the actual intrinsic pixel width of each image file, expressed with awsuffix. - Remove
sizesand keep density descriptors — If you only need to serve different resolutions for high-DPI screens at a fixed layout size, drop thesizesattribute and usexdescriptors.
When using width descriptors, the value must match the image file's actual intrinsic width in pixels. For example, if photo-640.jpg is 640 pixels wide, its descriptor should be 640w.
Examples
Invalid: sizes present with density descriptors
This triggers the error because 1x and 2x are density descriptors, but sizes requires width descriptors.
<img
src="photo-640.jpg"
srcset="photo-640.jpg 1x, photo-1280.jpg 2x"
sizes="(max-width: 600px) 100vw, 600px"
alt="A mountain landscape">
Invalid: sizes present with a bare candidate (no descriptor)
A candidate with no descriptor defaults to 1x, which is a density descriptor — still invalid when sizes is present.
<img
src="photo-640.jpg"
srcset="photo-640.jpg, photo-1280.jpg 2x"
sizes="(max-width: 600px) 100vw, 600px"
alt="A mountain landscape">
Fix: use width descriptors with sizes
Each candidate now specifies the intrinsic width of the image file. The browser uses the sizes value to determine which image to download.
<img
src="photo-640.jpg"
srcset="photo-320.jpg 320w, photo-640.jpg 640w, photo-1280.jpg 1280w"
sizes="(max-width: 600px) 100vw, 600px"
alt="A mountain landscape">
Alternative fix: remove sizes and use density descriptors
If you don't need the browser to choose images based on layout width — for example, the image always renders at a fixed CSS size — drop sizes and use x descriptors.
<img
src="photo-640.jpg"
srcset="photo-640.jpg 1x, photo-1280.jpg 2x"
alt="A mountain landscape">
Using width descriptors with source inside picture
The same rule applies to source elements inside a picture. If sizes is present, every candidate must use a w descriptor.
<picture>
<source
srcset="hero-480.webp 480w, hero-960.webp 960w, hero-1920.webp 1920w"
sizes="(max-width: 768px) 100vw, 50vw"
type="image/webp">
<img
src="hero-960.jpg"
srcset="hero-480.jpg 480w, hero-960.jpg 960w, hero-1920.jpg 1920w"
sizes="(max-width: 768px) 100vw, 50vw"
alt="A hero banner image">
</picture>
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
"
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.
When a <select> element is marked as required, the browser needs a way to determine whether the user has made a deliberate choice. The HTML specification requires that the first <option> element act as a placeholder — a non-selectable default that represents "no choice made." For the browser's constraint validation to work correctly, this placeholder option must have an empty value attribute (value=""), or it must have no text content at all.
This requirement only applies when all three conditions are met:
- The
<select>has arequiredattribute. - The
<select>does not have amultipleattribute. - The
<select>does not have asizeattribute with a value greater than1.
In this configuration, the <select> renders as a standard single-selection dropdown, and the first <option> with an empty value serves as the "please choose" prompt. If the user submits the form without changing the selection from this placeholder, the browser will block submission and display a validation message — just as it would for an empty required text input.
Why this matters
- Form validation: Without a proper placeholder option, the browser may consider the first option as a valid selection, allowing the form to submit even when the user hasn't actively chosen anything. This defeats the purpose of
required. - Accessibility: Screen readers and assistive technologies rely on standard patterns. A placeholder option clearly communicates to all users that a selection is expected.
- Standards compliance: The WHATWG HTML specification explicitly defines this constraint, and violating it produces a validation error.
How to fix it
- Add a placeholder
<option>as the first child of the<select>, withvalue=""and descriptive prompt text (e.g., "Choose an option"). - Alternatively, if you don't want a visible placeholder, the first
<option>can have no text content at all (<option value=""></option>), though this is less user-friendly. - Another approach is to add a
sizeattribute equal to the number of options, or add themultipleattribute — but these change the visual presentation from a dropdown to a list box, so they're only appropriate if that's the desired UI.
Examples
❌ Incorrect: first option has a non-empty value
<select required>
<option value="s">Small</option>
<option value="m">Medium</option>
<option value="l">Large</option>
</select>
Here, "Small" is preselected and has a non-empty value. The browser treats it as a valid choice, so required validation never triggers — the form can be submitted without the user making an active decision.
❌ Incorrect: placeholder option has a non-empty value
<select required>
<option value="none">Choose a size</option>
<option value="s">Small</option>
<option value="m">Medium</option>
<option value="l">Large</option>
</select>
The first option looks like a placeholder, but its value is "none" rather than empty. The validator flags this because the browser considers "none" a valid value.
✅ Correct: placeholder option with an empty value
<select required>
<option value="">Choose a size</option>
<option value="s">Small</option>
<option value="m">Medium</option>
<option value="l">Large</option>
</select>
The first <option> has value="" and serves as a clear prompt. If the user doesn't select a different option, form validation will prevent submission.
✅ Correct: placeholder option with no text content
<select required>
<option value=""></option>
<option value="s">Small</option>
<option value="m">Medium</option>
<option value="l">Large</option>
</select>
This also satisfies the constraint, though it may appear as a blank entry in the dropdown. It can work in cases where a <label> already makes the expected action clear.
✅ Correct: using a size attribute to avoid the requirement
<select required size="3">
<option value="s">Small</option>
<option value="m">Medium</option>
<option value="l">Large</option>
</select>
By adding size="3" (equal to the number of options), the <select> renders as a list box rather than a dropdown. The placeholder requirement no longer applies because no option is implicitly preselected — the user must click to choose. Note that this changes the visual appearance significantly.
The sandbox attribute is one of the most powerful security features available for <iframe> elements. When present with no value (or with specific tokens), it applies a strict set of restrictions to the embedded content — blocking scripts, form submissions, popups, same-origin access, and more. You selectively relax these restrictions by adding space-separated tokens like allow-scripts, allow-forms, or allow-popups.
The problem arises specifically when allow-scripts and allow-same-origin are used together. The allow-scripts token lets the embedded page execute JavaScript, while allow-same-origin lets it retain its original origin (rather than being treated as coming from a unique, opaque origin). With both enabled, the embedded page's JavaScript can access the parent page's DOM via window.parent (if they share the same origin) or, more critically, can use JavaScript to programmatically remove the sandbox attribute from its own <iframe> element. Once the attribute is removed, all sandboxing restrictions are lifted, making the sandbox attribute completely pointless.
This is why the W3C validator flags this combination — it's not technically invalid HTML, but it's a security anti-pattern that renders sandboxing ineffective. The WHATWG HTML specification explicitly warns against this combination for the same reason.
How to fix it
The fix depends on what the embedded content actually needs:
If the embedded page needs scripts but not same-origin access: Remove
allow-same-origin. The page can still run JavaScript but will be treated as a unique origin, preventing it from accessing cookies, storage, or the parent frame's DOM.If the embedded page needs same-origin access but not scripts: Remove
allow-scripts. The page retains its origin but cannot execute any JavaScript.If you believe both are required: Reconsider whether sandboxing is the right approach. If the embedded content truly needs both script execution and same-origin access, the
sandboxattribute provides no meaningful security benefit, and you may want to use other security mechanisms likeContent-Security-Policyheaders instead.
Always follow the principle of least privilege — only grant the permissions the embedded content strictly requires.
Examples
❌ Bad: using both allow-scripts and allow-same-origin
<iframe
src="https://example.com/widget"
sandbox="allow-scripts allow-same-origin">
</iframe>
This triggers the validator warning because the embedded page can use its script access combined with its preserved origin to break out of the sandbox entirely.
❌ Bad: both flags present alongside other tokens
<iframe
src="https://example.com/widget"
sandbox="allow-forms allow-scripts allow-same-origin allow-popups">
</iframe>
Even with additional tokens, the presence of both allow-scripts and allow-same-origin still defeats the sandbox.
✅ Good: allowing scripts without same-origin access
<iframe
src="https://example.com/widget"
sandbox="allow-scripts">
</iframe>
The embedded page can run JavaScript but is treated as a unique opaque origin, preventing it from accessing parent-page resources or removing its own sandbox.
✅ Good: allowing only the necessary permissions
<iframe
src="https://example.com/widget"
sandbox="allow-scripts allow-forms allow-popups">
</iframe>
This grants script execution, form submission, and popup capabilities while keeping the content sandboxed in a unique origin.
✅ Good: using an empty sandbox for maximum restriction
<iframe
src="https://example.com/widget"
sandbox="">
</iframe>
An empty sandbox attribute applies all restrictions — no scripts, no form submissions, no popups, no same-origin access, and more. This is the most secure option when the embedded content is purely static.
✅ Good: removing the sandbox when it provides no real benefit
<iframe
src="https://example.com/widget"
title="Example widget">
</iframe>
If you genuinely need both script execution and same-origin access, it's more honest to omit sandbox entirely and rely on other security measures, rather than including an attribute that provides a false sense of security.
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