Image Preview lets a page provide a lightweight preview while its main image loads. The browser replaces the preview when the main image would normally be displayed, including progressive content. If the main image is ready first, it skips the preview. The browser manages this lifecycle instead of requiring site-specific code.
The preview attribute accepts an image URL, compact representation, or color. Its reflected property also works on images created with new Image(). previewset supplies optional density-based candidates. Both attributes apply to <img> and <source> in <picture>, while existing source-selection rules continue to choose the main image.
<img
preview='url("photo-preview.avif")'
src="photo.avif"
width="1200"
height="800"
alt="A mountain reflected in a lake">
Image Preview does not define compact formats or their encoding and decoding algorithms. Examples such as blurhash("...") illustrate integration with separately specified formats.
The styling and image-reveal appendices sketch independent proposals, not dependencies of Image Preview. Earlier proposal versions explain the design's evolution.
Slow-loading images can leave empty regions on a page. A lightweight preview can help users recognize the content before the main image arrives, especially in galleries and feeds.
Sites implement previews with CSS backgrounds, source replacement, or stacked image and canvas elements. They must coordinate decoding, replacement, source changes, and transitions themselves.
The table below describes the major patterns used by current implementations. Representative examples are provided in the appendix.
| Pattern | Format | How it works | Examples and evidence |
|---|---|---|---|
| 1. CSS background preview with main image overlay | Base64 8×8 BMP; tiny JPEG embedded in SVG; BlurHash-derived WebP; ThumbHash-derived PNG | Set the preview as the CSS background of the target <img> or its wrapper. The main image paints over it or becomes opaque after loading. |
Unsplash; Next.js <Image>; Jesus Film with Next.js; Tommy Chow homepage gallery preview |
| 2. Decoded compact hash canvas | BlurHash decoded to canvas pixels | Decode the hash in script and paint it into a canvas occupying the image's display area. Load the target <img> independently; after it loads or decodes, reveal it and hide or remove the canvas. |
Minds; Mastodon; Misskey; Nextcloud Talk; Jellyfin Vue |
| 3. Single-image source replacement | Cloudinary-transformed raster image; Cloudinary selects the delivered format automatically | Assign the placeholder URL to one <img>, preload the main image resource, and replace the same element's src. |
Cloudinary training tool; Cico Jazz hero; Cloudinary plugin source |
| 4. Progressive stacked preview layers | BlurHash, small thumbnail, and larger preview | Stack progressively better representations and fade in each layer after it loads. | Nextcloud Photos |
<img> until the main image can be shown.<picture> branch and support density-based candidates without changing main-image selection.loading, decoding, or fetchpriority.<image> grammar, such as gradients, cross-fade(), or Paint Worklets, as preview values.The developer-needs document maps the needs and supporting evidence to the main proposal, independent designs, and open questions.
Add preview and previewset attributes to <img> and to <source> elements used by <picture>.
The preview is temporary visual content painted inside the image element. The selected image is the main resource chosen by normal src, srcset, sizes, and <picture> processing for the current image generation.
Authors should choose a preview that represents the selected image. The browser cannot verify this relationship; unrelated preview content can mislead users.
The proposal is divided along specification and implementation boundaries:
| Capability | Scope | Intended venue |
|---|---|---|
preview and its reflected property; typed URL, compact, and color inputs |
Core | WHATWG HTML, coordinating value syntax with CSS |
previewset and branch-specific preview selection in <picture> |
Core | WHATWG HTML |
| Preview fetching, decoding, display, replacement, and cancellation | Core | WHATWG HTML |
| Compact encodings such as BlurHash or ThumbHash | Independently specified formats and input bindings | Their respective specifications |
| Image-part styling and transitions | Independent exploratory proposal | CSS, coordinated with HTML |
image-reveal |
Independent exploratory proposal | CSS, coordinated with HTML |
| Dedicated preview events and metric integration | Open | To be determined |
The proposed value model distinguishes representations explicitly:
| Kind | Illustrative value | Processing |
|---|---|---|
| Image URL | url("thumb.avif") or url("data:image/avif;base64,...") |
Resolve the URL against the document base URL and use normal image fetching and decoding |
| Compact representation | blurhash("LEHV6nWB2yk8pyo0adR*.7kCMdnj") |
Use a separately specified format and its preview-value binding; no implied network request |
| Color | #ccd8e0 |
Paint a temporary solid-color representation; no fetch or image decoder |
URLs require url(...). Missing, empty, invalid, or unsupported values provide no preview; they are never reinterpreted as URLs.
An inline data URL avoids a separate request but increases document size.
The initial color model accepts self-contained CSS colors, such as hex colors, named colors, and rgb(...). It excludes currentColor, system colors, light-dark(), and var(), which would require style-resolution and invalidation rules.
preview and previewset are string-reflecting DOMString properties. Their getters return the authored attribute value, or an empty string when absent. Setters preserve invalid values; parsing determines whether they can supply a preview. Unlike src, these getters do not resolve URLs.
const image = new Image();
image.preview = 'url("thumb.avif")';
image.previewset = 'url("thumb.avif") 1x, url("thumb-2x.avif") 2x';
image.src = "photo.avif";
An <img> property reflects its own attribute, not a selected <source> declaration. There is no currentPreview property. See the IDL.
Preview rendering applies only to HTML <img>. <source> supplies declarations when used in <picture>, not in audio or video. The proposal does not extend <input type="image">, SVG <image>, <object>, or <embed>.
First select the main image using the existing <picture>, srcset, and sizes algorithms. The element supplying that image's source set owns its preview declarations:
<source> is selected, use only that source's preview and previewset.<img> element's declarations.<source> with no usable preview does not inherit the <img> preview. It might represent a different crop, subject, locale, or color scheme.<source>.For example, the dark-mode branch supplies its own preview:
<picture>
<source
media="(prefers-color-scheme: dark)"
srcset="chart-dark-800.avif 800w, chart-dark-1600.avif 1600w"
sizes="(max-width: 800px) 100vw, 800px"
preview='url("chart-dark-preview.avif")'>
<img
src="chart-light-800.avif"
srcset="chart-light-800.avif 800w, chart-light-1600.avif 1600w"
sizes="(max-width: 800px) 100vw, 800px"
preview='url("chart-light-preview.avif")'
width="800" height="500"
alt="Monthly rainfall over the past year">
</picture>
The dark branch uses its own preview, avoiding a light-preview flash. The same rule handles different crops and other art direction; the browser does not recolor or recrop previews. Authors can repeat a shared preview on branches where it is appropriate.
The same preview can represent every resolution in an <img> or <source> source set. Authors do not need a separate preview for each main-image URL.
For different preview sampling densities, previewset provides a list of typed preview values with positive x density descriptors:
<img
src="forest-1600.avif"
srcset="forest-800.avif 800w, forest-1600.avif 1600w"
sizes="(max-width: 800px) 100vw, 800px"
preview="#667788"
previewset='url("forest-preview-32.avif") 1x,
url("forest-preview-64.avif") 2x'
width="1600" height="1000"
alt="A forest lake">
1x identifies the author's baseline preview quality; 2x provides higher sampling density. Neither promises a sharp image at the element's display size or changes its intrinsic dimensions.
The proposed selection rules are:
1x; densities must be positive, finite, and unique. Width descriptors and a separate previewsizes attribute are not included.srcset with x descriptors, including device density and browser resource policy. Do not fetch every candidate or cycle through them as quality steps.preview. A usable list takes precedence; preview is not an implicit additional 1x candidate.preview.The list accepts the same value kinds as preview. Commas inside functions or strings are not candidate separators. The main image's sizes does not apply to preview-resource widths, and the browser does not prefetch URLs to check format support.
Changes in branch or preview selection participate in the source-update lifecycle, even when the main-image URL stays the same.
Preview processing integrates with HTML's update the image data algorithm:
Without a valid main-image candidate, pending preview work becomes obsolete and any displayed preview is removed. Normal no-source or broken-image rendering applies; a preview is not a standalone image or fallback source.
When a URL preview resolves to the selected-image URL, the browser may reuse the same fetch and decode rather than perform duplicate work. The resource is treated as the selected image and does not participate in the preview lifecycle.
Selected-image discovery, selection, and request creation must not wait for preview work. Preview processing is best-effort and decoding is asynchronous, although an additional request can still compete for bandwidth.
fetchpriority and decoding continue to describe the selected image. There is no preview-specific priority control. The browser may skip a preview that is unlikely to be useful before the selected image appears.
The browser fetches a URL preview as an image resource, following the same fetching and security rules as other <img> requests. These include URL parsing, CORS, credentials, referrer policy, Content Security Policy img-src, mixed content, cookies, HTTP caching, network partitioning, and service-worker handling. Request policies come from the host <img>, even for declarations on <source>. A separately fetched preview produces a Resource Timing entry under the usual image-request rules, with initiatorType equal to "img".
Literal colors and compact payloads do not initiate requests or produce Resource Timing entries. Their processing rules are covered under Security and compact formats.
Ready to present means that content is available and the applicable rendering policy permits displaying it. This is determined before painting. The core uses native main-image timing, including progressive rendering.
The proposed flow for an initial presentation is:
flowchart LR
A[Preview and selected-image processing active] -->|Selected image ready to present first| I[Selected image renders normally]
A -->|Preview ready first| P[Preview displayed]
P -->|Native selected-image presentation permitted| I
A -->|Preview unavailable| N[No preview displayed]
N -->|Selected image ready to present| I
The numbered steps below are the text alternative for the diagram:
<img>.The core handoff does not animate.
loading="lazy" also defers preview processing. Independent preview loading remains an open question.image-animation value applies to both preview and selected-image animations. Replacing the preview ends its playback. There are no preview-specific animation controls.<img> state. Discarding the document makes preview work obsolete. Any companion styling proposal must separately define its transition lifecycle.Script can update the reflected preview property and the image source, as in a dynamic image gallery:
const image = document.querySelector("#gallery-image");
image.preview = photo.previewValue;
image.src = photo.url;
photo.previewValue contains a value such as url("thumb.avif"), not a bare URL. If a <source> supplies the selected image, update that source's preview declarations instead.
Changes to preview, previewset, src, srcset, sizes, and relevant <source> elements or selection conditions participate in HTML's existing image-data update processing. An image generation identifies the selected branch, main-image request, and chosen preview value; it is not a script-visible object.
Results from superseded work cannot newly replace current content. The browser cancels obsolete fetching and decoding when possible. In particular:
src: an already-displayed preview remains visible while its replacement loads. Canceling superseded work does not itself remove displayed content.The browser temporarily displays the preview inside the image element, but the selected image remains the element's current image resource. Standard HTMLImageElement state and events, including currentSrc, complete, naturalWidth, naturalHeight, decode(), load, and error, continue to describe the selected image.
The core exposes no preview-specific lifecycle events or status. Resource Timing describes separately fetched previews but does not report whether they decoded or were displayed.
The preview does not determine the <img> element's intrinsic dimensions or layout size. Normal CSS, width, height, aspect-ratio, and selected-image sizing rules apply, including existing <source> dimension handling.
A decoded image preview is fitted to the content box using object-fit and object-position. Compact formats define their decoded dimensions and aspect ratio. A color has no intrinsic dimensions and fills the box. Normal image-content clipping applies.
A preview may be fetched and decoded before a nonzero content box exists, but cannot be painted until one exists and the preview remains eligible. Authors should reserve space with width and height, aspect-ratio, or CSS sizing. The selected image's intrinsic dimensions can still affect layout under normal rules.
The preview is not exposed through drawImage(), createImageBitmap(), or other image-extraction APIs. These APIs follow their existing selected-image algorithms independently of what is presented; they never substitute preview pixels.
On replacement, the preview is removed rather than left behind transparent parts of the selected image. This distinguishes a color preview from a persistent background. The independent styling design explores keeping outgoing content available during transitions.
Context menus, drag operations, saving, accessibility, and other semantics continue to identify the selected image and its src/currentSrc, not the preview.
In a browser that does not implement Image Preview, the unrecognized preview and previewset attributes have no effect and the existing image source attributes continue to work.
Script can detect support for the reflected property:
const supportsImagePreview =
"preview" in HTMLImageElement.prototype;
This detects API support, not support for individual compact formats. Per-format capability detection remains an open question. previewset and the <source> properties can likewise be checked on their corresponding prototypes. Unsupported representations follow the candidate-selection and preview-unavailable lifecycle rules.
Developer tools may report that a preview was skipped, blocked, unsupported, or failed, but such diagnostics are not a web-observable API and are outside the HTML feature definition.
Each compact format needs a separate specification and a preview-value binding that defines its function name, payload syntax, and decoding behavior.
BlurHash and ThumbHash are examples of representations that could be integrated. The function below is illustrative, pending that work:
<img
preview='blurhash("LEHV6nWB2yk8pyo0adR*.7kCMdnj")'
src="/images/portrait.jpg"
width="800"
height="1000"
alt="Portrait of a person">
A format binding must define encoded-size, output-dimension, memory, and processing limits; color and alpha behavior; malformed-input handling; and a prohibition on nested network requests. It must not depend on author-provided decoder script.
Should authors be able to query support for individual preview formats?
Should authors be able to load and display a preview before a lazy-loaded selected image?
Loading the preview eagerly could reduce visible pop-in when an image approaches the viewport. For an inline preview, the bytes are already part of the document, although decoding and retaining the preview still have CPU and memory costs. For an external preview, eager loading could fetch resources for images the user never sees and compete with more important requests.
It remains open whether inline and external previews should follow the same loading policy and how authors could control them independently.
If image A is already presented while image B loads, should a newly ready preview B replace A, or should A remain until B can be presented? The former gives early context for the new resource; the latter avoids replacing an existing image with lower-detail content.
The lifecycle must define this choice. The rules against displaying stale results or old-branch previews apply either way.
Authors can decide whether to provide a preview, including from script. Whether browsers should offer a rule such as "use previews only on slow connections" remains open.
These earlier designs explain the proposal's evolution. Some remain useful alternatives for specific use cases.
previewsrc and previewSrcThe earlier API accepted one image URL and used URL reflection, like src:
<img previewsrc="thumb.avif" src="photo.avif"
width="800" height="600" alt="A lake">
Compact representations would require a separately specified image format. A hypothetical data URL could look like this:
<img previewsrc="data:image/blurhash,LEHV6nWB2yk8pyo0adR*.7kCMdnj"
src="photo.avif" width="800" height="600" alt="A lake">
The URL-only design reused ordinary image fetching and decoding, but required every preview to be represented as an image resource, whether referenced by an external URL or embedded in a data URL. Even a solid-color preview needed an image resource rather than a color value.
General image-format support would also need to address compact encodings in <img src>, CSS background-image, and other platform primitives that load image resources. Low-detail previews may not suit all these contexts. Keeping decoding preview-only would require special rules.
preview instead accepts preview-specific compact-format bindings and colors without requiring support in other image consumers. It retains url(...) for ordinary image resources and reflects all values as authored strings.
<img>The earlier draft used one previewsrc on <img> for all main-image candidates, reducing scope but risking mismatched crops or color schemes. The revised design supports source-specific previews and density-based previewset, with no fallback from a selected <source> to the <img> preview.
The earlier design retained a displayed preview until the main image's first frame was complete and decoded, matching scripted decode() handoffs but hiding progressive content. The core now preserves native timing regardless of whether a preview appears. The independent image-reveal: complete-frame sketch makes waiting an explicit author choice.
The earlier sketch proposed automatic Element Scoped View Transitions to animate from a preview snapshot to the main image's complete decoded first frame:
img:active-image-preview-transition::view-transition-old(root) {
animation: 200ms ease-out both image-preview-fade-out;
}
img:active-image-preview-transition::view-transition-new(root) {
animation: 200ms ease-in both image-preview-fade-in;
}
@keyframes image-preview-fade-out {
to { opacity: 0; }
}
@keyframes image-preview-fade-in {
from { opacity: 0; }
}
The independent CSS styling sketch is preferred because it can style waiting content, previews, and the main image throughout loading, not just during a handoff. Waiting and main-image styling also work without Image Preview.
Scoped View Transitions could still complement these parts for image-to-image replacements without a phase change, but would require additional resource-triggered integration.
lowsrc attributeThe historical lowsrc attribute associated a temporary low-resolution image with the main image. Image Preview adapts that idea to modern HTML while retaining the costs of a separate resource.
See Historical lowsrc research for its standards history, Gecko removal, and available usage evidence.
A CSS property could provide the preview instead of, or alongside, the HTML attribute:
.gallery-image {
image-preview-source:
linear-gradient(135deg, #68788c, #bcc4cc);
}
Compared with the HTML preview input, this has several advantages:
<image> syntax.It also has disadvantages:
HTMLImageElement property, scripts must read and update the preview through styles instead of the image API.CSS would still need display and replacement rules. The proposed division keeps preview content in HTML for early discovery and uses CSS for appearance and waiting decoration.
The browser-managed preview lifecycle could be exposed only through JavaScript:
<img
id="gallery-image"
src="/images/forest-1600.avif"
width="1600"
height="1067"
alt="Forest trail in autumn">
const image = document.querySelector("#gallery-image");
image.setPreviewSource("/images/forest-32.avif");
This would support imperative options but require script before the browser could discover the preview. The reflected preview attribute instead supports both server-rendered markup and script updates.
<picture>A special media="poster" value on a <source> element could identify that source as the preview rather than as a selected-image candidate:
<picture>
<source media="poster" srcset="/images/forest-preview.avif">
<source
media="(min-width: 800px)"
srcset="/images/forest-1600.avif">
<img
src="/images/forest-800.avif"
alt="Forest trail in autumn">
</picture>
This keeps preview and main-image sources together, but requires <picture> for every preview. It also repurposes media to identify a source's role rather than evaluate a media query.
The proposed preview and previewset attributes instead annotate the existing <img> or selected <source>, without giving media a special role or requiring <picture> when no art direction is needed.
poster attributeThe <video> element uses poster to identify an image shown before video data is available. The same attribute could be added to <img>:
<img
poster="/images/forest-32.avif"
src="/images/forest-1600.avif"
alt="Forest trail in autumn">
This would reuse a familiar name for temporary content, while still requiring image-preview lifecycle rules and a reflected property. Related work in whatwg/html#12585 proposes responsive video posters through a child <img>, not a poster attribute on <img>.
This draft uses preview because the value can be a compact payload or color, unlike the existing URL-valued poster.
Progressive JPEG, interlaced formats, and incremental decoding can reveal an image as its bytes arrive. They avoid a separate request and can be preferable when the format, delivery path, and browser produce useful intermediate content.
They do not replace every preview use case:
An independent preview can reuse a small image, travel inline, or be cached separately. It adds transfer or processing costs and may compete for bandwidth. Image Preview complements progressive delivery rather than replacing it; authors should omit previews when progressive rendering already provides an adequate experience.
These alternatives combine native decoding, existing transition APIs, and author script differently.
The browser could decode compact formats while authors manage replacement through ordinary image or canvas APIs. This example assumes a separately specified format and hypothetical media type:
<div class="image-frame">
<img
class="preview"
src="data:image/blurhash,LEHV6nWB2yk8pyo0adR*.7kCMdnj"
alt="">
<img
class="final"
src="/images/portrait.jpg"
alt="Portrait of a person">
</div>
This could remove decoder libraries and reduce script size and decoding cost. Authors would still manage loading, replacement, cancellation, source changes, failures, and accessibility across elements.
The platform could provide native compact decoding and scoped View Transitions but leave the handoff to author script:
<img
id="photo"
src="data:image/blurhash,LEHV6nWB2yk8pyo0adR*.7kCMdnj"
alt="Portrait of a person">
const photo = document.querySelector("#photo");
const finalImage = new Image();
finalImage.src = "/images/portrait.jpg";
await finalImage.decode();
const transition = photo.startViewTransition(async () => {
photo.src = finalImage.src;
await photo.decode();
});
await transition.finished;
This reuses a general transition API but leaves preloading, stale-update handling, failure, and cancellation to each page. Image Preview instead lets the browser manage loading and replacement.
The browser could manage the preview lifecycle but invoke an author-registered decoder when it does not support the preview's media type:
<img
preview='url("data:image/blurhash,LEHV6nWB2yk8pyo0adR*.7kCMdnj")'
src="/images/portrait.jpg"
alt="Portrait of a person">
navigator.imagePreview.registerDecoder(
"image/blurhash",
async (bytes, options) => decodeBlurHash(bytes, options)
);
This would allow new formats without native decoders, but make previews depend on script loading and execution. It also requires rules for decoder lifetime, cancellation, security, failures, dimensions, color, and resource limits.
The preview and selected image share one <img>, one accessibility node, and one alt value. The preview must represent that same content without creating a second announcement.
Showing a preview can replace the visible loading-time alt text that some browsers display. The description remains available to assistive technologies, but sighted users may lose information that a blurred preview cannot convey.
The core handoff does not animate. Independent styling must respect reduced-motion preferences and keep informative loading indicators distinguishable from their backgrounds. Encoded preview animations follow existing image-animation controls. Branch-specific previews can avoid a light-preview flash before dark-mode content.
The proposal adds no user-facing text and no language-sensitive processing.
The guidance for localized or direction-dependent image candidates is covered by Scope and responsive images.
Fetching an external URL preview discloses an ordinary image request and its metadata to the serving origin. Unselected branches and candidates are not fetched. Literal colors and compact payloads make no requests.
Resource Timing exposes ordinary fetch and cache information under existing image-request protections. The core adds no dedicated signals for preview readiness, dimensions, decoding, display, handoff timing, or the reason a preview was skipped.
Candidate selection can reveal format support indirectly. Rejecting an unsupported compact value may cause a URL fallback request, observable through network information or by the server.
A site can also time decoding or drawing the same resource through another Image, createImageBitmap(), canvas, or WebGL. Shared caches may affect those timings, but they do not directly measure the internal preview decode or reveal whether it was displayed.
The styling sketch would expose coarse presentation state through selectors and inspectable transitions. Its independent proposal requires privacy analysis of timing and format-support inferences.
URL previews follow the host image's fetch protections, including CSP img-src and mixed-content rules. Unsupported or malformed values are never reinterpreted as URLs.
Literal colors and compact payloads are inline author data. They do not trigger img-src request checks, execute script, resolve style variables, or fetch nested resources. Parsing and processing must be bounded.
Decoding must enforce the format-specific limits. Obsolete work must be abortable, and repeated source changes must not create unbounded concurrent decoding. Malformed or adversarial input follows the preview-unavailable lifecycle. Each compact-format binding requires its own security review.
Preview pixels are unavailable to image-extraction APIs, so their origin does not independently affect canvas origin cleanliness. Canvas access follows the selected image's origin-clean rules.
| Stakeholder | Signal | Source |
|---|---|---|
| Chromium | No position requested | — |
| Gecko | No position requested | — |
| WebKit | No position requested | — |
| WHATWG | No review requested | — |
| Web developers | Requests and concerns about art direction, lazy previews, styling, progressive rendering, formats, and visible alt text; not consensus on this API | Developer needs and evidence |
lowsrc sourcesImageIHTMLImgElement::lowsrcHTMLImageElementlowsrc attributelowsrc supportlowsrc compatibilitylowsrc property compatibilityThe authors acknowledge the BlurHash and ThumbHash projects and the application and framework developers whose implementations informed this proposal. Their inclusion does not imply endorsement.
The properties use the string-reflection rules described above:
partial interface HTMLImageElement {
[CEReactions] attribute DOMString preview;
[CEReactions] attribute DOMString previewset;
};
partial interface HTMLSourceElement {
[CEReactions] attribute DOMString preview;
[CEReactions] attribute DOMString previewset;
};
This independent, exploratory sketch addresses DN-07 through DN-09 and part of DN-11. Waiting and main-image styling work without Image Preview; preview-specific parts and phases are extensions.
<img>
└── ::image-group shared clipping and isolated blending
├── ::image-placeholder waiting decoration
├── ::preview preview content
└── ::image ordinary image content
These internal painting parts do not add DOM children, size the host, or duplicate the image. The <img> keeps its layout, borders, padding, accessibility, APIs, and native fallback. The group occupies its content box without requiring position: relative.
The read-only :image-phase(...) selector identifies the content selected for display, not general loading/error states (w3c/csswg-drafts#7467).
| Phase | Condition |
|---|---|
none |
No image or preview is selected for display, and no initial presentation is pending; native fallback applies |
waiting |
Image pending, including lazy deferral, with no preview or retained image target |
preview |
A usable preview is selected for display |
image |
Ordinary image content, including progressive or retained content |
Phases are chosen before painting, independently of author opacity or visibility, and do not signal loading, decoding, or transition completion. Changing or removing src need not change the phase if retained content or another selected source remains. Author styles must not make decoding wait for a phase change that decoding itself would trigger.
These defaults require browser-provided parts; they are not a polyfill:
| Part or condition | Default style |
|---|---|
::image-group |
overflow: clip; isolation: isolate; pointer-events: none |
| All three content parts | display: block; opacity: 1; mix-blend-mode: normal; transition: none; animation: none |
Placeholder outside waiting; preview outside preview |
display: none !important; opacity: 0 |
| Placeholder/preview entry | @starting-style sets opacity: 0 |
The group, preview, and image parts inherit object-fit and object-position. The important rule prevents permanent retention of inactive parts, but transitions can temporarily override it. The UA never hides ::image based on a phase. Without previews or author part styling, ordinary image behavior is unchanged.
<img class="photo"
src="photo.avif"
preview='url("thumb.avif")'
width="800" height="600"
alt="A mountain reflected in a lake">
.photo::image-placeholder {
background: linear-gradient(135deg, #e2e8f0, #cbd5e1);
}
.photo::preview {
filter: blur(4px);
}
.photo::image-placeholder,
.photo::preview,
.photo::image {
mix-blend-mode: plus-lighter;
}
.photo::image-placeholder,
.photo::preview {
transition:
opacity 180ms,
display 180ms allow-discrete;
}
.photo::image {
transition: opacity 180ms;
}
.photo:not(:image-phase(image))::image {
opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
.photo::image-placeholder,
.photo::preview,
.photo::image {
transition: none;
animation: none;
}
}
This example adds a gradient, preview-only blur, and an author-controlled crossfade. Skip unavailable stages rather than creating a placeholder flash. In the isolated group, plus-lighter supports alpha-correct blending when layer opacities are complementary; different timings or interrupted transitions are not automatically normalized. Isolation neither clips content nor makes it opaque.
The browser must support this retention lifecycle:
display: none, allowing an allow-discrete display transition to retain it for exit effects.Outgoing animations can remain live without delaying selected-image loading events or decode(). Releasing a rendering reference does not require eviction from shared caches.
Open work includes object-fitting geometry, rounded corners, filters, excessive or stalled exits, resource pressure, freezing, and errors. Outgoing decoration must not indefinitely obscure native fallback.
Unchanged phases cannot trigger image-to-image or preview-to-preview transitions. Resource-triggered transitions and source replacement need separate design, alongside privacy and accessibility review.
This independent sketch addresses DN-10. It controls when the main image can appear, not how it animates, and works without previews or CSS parts.
.photo {
image-reveal: normal;
}
.photo.quality-first {
image-reveal: complete-frame;
}
| Value | Proposed behavior |
|---|---|
normal (initial value) |
Preserve native presentation timing, including progressive rendering |
complete-frame |
Wait until native presentation is permitted and the first frame is complete and decoded |
The policy applies when replacing a placeholder, preview, or retained image. For animated images, complete-frame waits for a complete decoded first frame, not the whole animation.
Source selection, fetching, load, decode(), and image-extraction APIs are unchanged; the property neither creates nor removes ::image. Policy changes cannot hide an already-presented image generation. Switching to normal while waiting may reveal progressive content.
Failures use native error/alt rendering. Inheritance, dynamic changes, retained-content handling, and HTML integration need separate specification.
lowsrc researchThis section records the evidence used to evaluate lowsrc as prior art. It does not establish a single reason why the feature declined across the web.
LOWSRC originated as a Netscape extension and was later implemented by Internet Explorer. It allowed an <img> to specify a low-resolution resource that was displayed before the main src resource.
The attribute was never part of the HTML standard. DOM Level 1 exposed the lowSrc property, but DOM Level 2 removed it.
lowsrc content attribute non-conforming;HTMLImageElement.lowsrc as a URL-reflecting compatibility property.Mozilla bug 92453 documents Gecko's 2001 decision to remove its lowsrc loading behavior. The discussion identifies implementation problems, the feature's non-standard status, and questions about its usefulness.
Historical browser bug reports provide some evidence of lowsrc markup or property usage, including Ameritrade, gimp.org, and CNN. However, no reliable data on its past or current usage was found.
No evidence was found that progressive formats or later responsive-image features caused lowsrc to decline.
Clearer specification alone does not demonstrate user benefit or adoption. The same underlying risks still require evaluation:
The lowsrc name is not reused because browsers already expose HTMLImageElement.lowsrc. Reusing it would make property-based feature detection ambiguous and could give existing markup new loading behavior.
These examples correspond to patterns 1 and 2 in How authors provide previews today.
The Next.js image placeholder demo renders a single <img> whose CSS background is an inline SVG containing a tiny JPEG preview:
<img
alt="Mountains"
src="/_next/image?url=...&w=1920&q=75"
srcset="/_next/image?url=...&w=750&q=75 1x,
/_next/image?url=...&w=1920&q=75 2x"
style="
background-image: url('data:image/svg+xml,...');
background-size: cover;
background-position: 50% 50%;
background-repeat: no-repeat;
">
After the selected image loads and decoding settles, Next.js removes the background preview. See the demo source, background construction, and load handoff.
On this Minds post, the site decodes BlurHash data into pixels and paints them into a canvas associated with the selected image. When the image loads, Minds fades and removes the canvas. See the Minds BlurHash directive.
The resulting structure is equivalent to:
<div class="image-frame">
<canvas class="preview" width="32" height="24"></canvas>
<img class="final" src="photo.jpg" alt="...">
</div>