CSS Images Module Level 4 CSS Images Module Level 4 Editor’s Draft 23 December 2025 More details about this document This version: Latest published version: Previous Versions: Feedback: CSSWG Issues Repository Tracker Inline In Spec Editors: Tab Atkins Jr. Google Elika J. Etemad / fantasai Apple Lea Verou Invited Expert Suggest an Edit for this Spec: GitHub Editor Delta Spec: yes Test Suite: World Wide Web Consortium W3C liability trademark and permissive document license rules apply. Abstract This module contains the features of CSS level 4 relating to the type and replaced elements. It includes and extends the functionality of CSS level 2 [CSS2] and in the previous level of this specification [css-images-3] The main extensions compared to "CSS Images Module Level 3" [css-images-3] are several additions to the type, such as the image() notation, the element() notation, and conic gradients. This level is currently maintained as a diff spec over the level 3 module. CSS is a language for describing the rendering of structured documents (such as HTML and XML) on screen, on paper, etc. Status of this document This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress. Please send feedback by filing issues in GitHub (preferred), including the spec code “css-images” in the title, like this: “[css-images] …summary of comment… ”. All issues and comments are archived Alternately, feedback can be sent to the ( archived ) public mailing list www-style@w3.org This document is governed by the 18 August 2025 W3C Process Document 1. Introduction This section is not normative. This module introduces additional ways of representing 2D images, for example as a URL with color fallback as conic gradients or as the rendering of another element in the document 1.1. Value Definitions This specification follows the CSS property definition conventions from [CSS2] using the value definition syntax from [CSS-VALUES-3] Value types not defined in this specification are defined in CSS Values & Units [CSS-VALUES-3] Combination with other CSS modules may expand the definitions of these value types. In addition to the property-specific values listed in their definitions, all properties defined in this specification also accept the CSS-wide keywords as their property value. For readability they have not been repeated explicitly. 2. 2D Image Values: the type The value type denotes a 2D image. It can be a url reference image notation or gradient notation Its syntax is: image () image-set () cross-fade () element () An can be used in many CSS properties, including the background-image list-style-image cursor properties [CSS2] (where it replaces the component in the property’s value). In some cases an image is invalid, such as a pointing to a resource that is not a valid image format or that has failed to load. An invalid image is rendered as a solid-color transparent image with no natural dimensions However, invalid images can trigger error-handling clauses in some contexts. For example, an invalid image in list-style-image it is treated as none allowing the list-style-type to render in its place. [CSS2] While an image is loading, is a loading image Loading images are not invalid images but have similar behavior: they are rendered as a solid-color transparent image with no natural dimensions and may trigger fallback rendering in contexts that offer it, but must not trigger loading of fallback resources. Alternately, if a loading image happens to be replacing an already-loaded image (for example due to changes in the document or style sheet) and the UA is tracking this information, it may continue to render the already-loaded image in place of the loading image Partially-loaded images (whose natural dimensions are known, but whose image data is not fully loaded) may be either treated as loading images or as loaded images rendered with partial data. For example, a UA may render an interlaced GIF in place as soon as its first pass of pixel data has loaded or even as soon as the image header (which contains sizing data) has parsed and refresh the rendering as more data loads; or it may wait until the entire image has loaded before using it. computed value is the specified value with any s, s, and s computed. 2.1. Image File Formats At minimum, the UA must support the following image file formats when referenced from an value, for all the properties in which using is valid: PNG, as specified in [PNG] SVG, as specified in [SVG11] using the secure static mode (See [SVG-INTEGRATION] If the UA supports animated s, SVG, as specified in [SVG11] using the secure animated mode (See [SVG-INTEGRATION] The UA may support other file formats as well. 2.2. Image References: the url() notation Note: No change from [css-images-3] 2.3. Fetching External Images To fetch an external image for a stylesheet given a url and a CSS declaration block declaration fetch a style resource given url with ruleOrDeclaration being declaration destination "image", CORS mode "no-cors", and processResponse being the following steps given response res and null, failure or a byte stream byteStream If byteStream is a byte stream, load the image from the byte stream. 2.4. Resolution/Type Negotiation: the image-set() notation Delivering the most appropriate image resolution for a user’s device can be a difficult task. Ideally, images should be in the same resolution as the device they’re being viewed in, which can vary between users. However, other factors can factor into the decision of which image to send; for example, if the user is on a slow mobile connection, they may prefer to receive lower-res images rather than waiting for a large proper-res image to load. The image-set() function allows an author to ignore most of these issues, simply providing multiple resolutions of an image and letting the UA decide which is most appropriate in a given situation. This solution assumes that resolution is a proxy for filesize, and therefore doesn’t appropriately handle multi-resolution sets of vector images, or mixing vector images with raster ones (e.g. for icons). For example, use a vector for high-res, pixel-optimized bitmap for low-res, and same vector again for low-bandwidth (because it’s much smaller, even though it’s higher resolution). The syntax for image-set() is: image-set () image-set || type We should add "w" and "h" dimensions as a possibility to match the functionality of HTML’s picture Each inside image-set() represents a The image-set() function can not be nested inside of itself, either directly or indirectly (as an argument to another type). Each defines a possible image for the image-set() function to represent, composed of three parts: An image reference (required). This can be a URL, or a CSS generated image, such as a linear-gradient() (optional). This is used to help the UA decide which to choose. If the image reference is for a raster image, it also specifies the image’s natural resolution overriding any other source of data that might supply a natural resolution If not specified, it behaves as 1x for the purpose of selecting which to use. It also defaults the image’s natural resolution to 1x but if some other source of data supplies a natural resolution that resolution must be honored instead. type( function (optional), specifying the image’s MIME type in the If the when parsed as a valid MIME type string is either not valid, or is valid but doesn’t specify a supported image format, the does not define a valid option. (This has no effect on the validity of the image-set() function.) It does not have any effect on the image itself; an like url "picture.png" type "image/jpeg" is valid, and if chosen will display the linked PNG image, even though it was declared to be a JPEG. If not specified, it has no effect on the Tests image-set-all-options-invalid.html (live test) (source) image-set-calc-x-rendering-2.html (live test) (source) image-set-calc-x-rendering.html (live test) (source) image-set-computed.sub.html (live test) (source) image-set-conic-gradient-rendering.html (live test) (source) image-set-content-rendering.html (live test) (source) image-set-dpcm-rendering.html (live test) (source) image-set-dpi-rendering-2.html (live test) (source) image-set-dpi-rendering.html (live test) (source) image-set-dppx-rendering.html (live test) (source) image-set-empty-url-rendering.html (live test) (source) image-set-first-match-rendering.html (live test) (source) image-set-linear-gradient-rendering.html (live test) (source) image-set-negative-resolution-rendering-2.html (live test) (source) image-set-negative-resolution-rendering-3.html (live test) (source) image-set-negative-resolution-rendering.html (live test) (source) image-set-no-res-rendering-2.html (live test) (source) image-set-no-res-rendering.html (live test) (source) image-set-no-url-rendering.html (live test) (source) image-set-parsing.html (live test) (source) image-set-radial-gradient-rendering.html (live test) (source) image-set-rendering-2.html (live test) (source) image-set-rendering.html (live test) (source) image-set-repeating-conic-gradient-rendering.html (live test) (source) image-set-repeating-linear-gradient-rendering.html (live test) (source) image-set-repeating-radial-gradient-rendering.html (live test) (source) image-set-resolution-001.html (live test) (source) image-set-resolution-002.html (live test) (source) image-set-resolution-003.html (live test) (source) image-set-type-first-match-rendering.html (live test) (source) image-set-type-rendering-2.html (live test) (source) image-set-type-rendering-3.html (live test) (source) image-set-type-rendering.html (live test) (source) image-set-type-skip-unsupported-rendering.html (live test) (source) image-set-type-unsupported-rendering-2.html (live test) (source) image-set-type-unsupported-rendering.html (live test) (source) image-set-unordered-res-rendering.html (live test) (source) image-set-zero-resolution-rendering-2.html (live test) (source) image-set-zero-resolution-rendering.html (live test) (source) An image-set() function contains a list of one or more s, and must select only one of them to determine what image it will represent: First, remove any s from the list that specify an unknown or unsupported MIME type in their type() value. Second, remove any s from the list that have the same as a previous option in the list. If there are no left at this point, the function represents an invalid image Finally, among the remaining s, make a UA-specific choice of which to load, based on whatever criteria deemed relevant (such as the resolution of the display, connection speed, etc). The image-set() function then represents the of the chosen UAs may change which they wish to use for a given image-set() over the lifetime of the page, if the criteria used to determine which option to choose change significantly enough to make it worthwhile in the UA’s estimation. This example shows how to use image-set() to provide an image in three versions: a "normal" version, a "high-res" version, and an extra-high resolution version for use in high-quality printing (as printers can have extremely high resolution): background-image image-set "foo.png" "foo-2x.png" "foo-print.png" 600 dpi ); This example shows use of the type() function to serve multiple versions of the same image in both new, higher-quality formats, and older, more widely-supported formats: background-image image-set "foo.avif" type "image/avif" ), "foo.jpg" type "image/jpeg" ); Note that the AVIF image is given first; since both images have the same resolution (defaulting to 1x since it’s unspecified), the JPEG image, coming second, is automatically dropped in UAs that support AVIF images. In older UAs, however, the AVIF image is ignored (because the UA knows it doesn’t support "image/avif" files), and so the JPEG is chosen instead. Raster images can be mixed with vector images, or even CSS generated images. For example, in this code snippet a high-resolution image with subtle details is used on screens that can do it justice, while an ordinary CSS linear-gradient() is used instead for low-resolution situations: background-image image-set linear-gradient cornflowerblue white url "detailed-gradient.png" ); 2.5. Image Fallbacks and Annotations: the image() notation The image() function allows an author to: use media fragments to clip out a portion of an image use a solid color as an image fallback to a solid-color image, when the image at the specified url can’t be downloaded or decoded automatically respect the image orientation specified in the image’s metadata The image() notation is defined as: image () image ltr rtl used in image() represents a As usual for URLs in CSS, relative URLs are resolved to an absolute URL (as described in Values & Units [CSS-VALUES-3] when a specified image() value is computed. If the image has an orientation specified in its metadata, such as EXIF, the UA must rotate or flip the image to correctly orient it as the metadata specifies. 2.5.1. Image Fallbacks If both a URL and a are specified in image() then whenever the URL represents an invalid image or loading image the image() function renders as if the URL were not specified at all; it generates a solid-color image as specified in § 2.5.3 Solid-color Images If just a URL is specified (no and it represents an invalid image or loading image the image() function represents the same. Tests css-image-fallbacks-and-annotations.html (live test) (source) css-image-fallbacks-and-annotations002.html (live test) (source) css-image-fallbacks-and-annotations003.html (live test) (source) css-image-fallbacks-and-annotations004.html (live test) (source) css-image-fallbacks-and-annotations005.html (live test) (source) The fallback color can be used to ensure that text is still readable even when the image fails to load. For example, the following legacy code works fine if the image is rectangular and has no transparency: body color black background white p.special color white background url "dark.png" black When the image doesn’t load, the background color is still there to ensure that the white text is readable. However, if the image has some transparency, the black will be visible behind it, which is probably not desired. The image() function addresses this: body color black background white p.special color white background image "dark.png" black ); Now, the black won’t show at all if the image loads, but if for whatever reason the image fails, it’ll pop in and prevent the white text from being set against a white background. 2.5.2. Image Fragments When a URL specified in image() represents a portion of a resource (e.g. by the use of media fragment identifiers that portion is clipped out of its context and used as a standalone image. For example, given the following image and CSS: background-image image 'sprites.svg#xywh=40,0,20,20' ...the background of the element will be the portion of the image that starts at (40px,0px) and is 20px wide and tall, which is just the circle with a quarter filled in. So that authors can take advantage of CSS’s forwards-compatible parsing rules to provide a fallback for image slices, implementations that support the image() notation must support the xywh=# form of media fragment identifiers for images specified via image() [MEDIA-FRAGS] Note that image fragments can also be used with the url() notation. However, a legacy UA that doesn’t understand the media fragments notation will ignore the fragment and simply display the entirety of the image. Since the image() notation requires UAs to support media fragments, authors can take advantage of CSS’s forward-compatible parsing rules to provide a fallback when using an image fragment URL: background-image url 'swirl.png' ); /* old UAs */ background-image: image 'sprites.png#xywh=10,30,60,20' ); /* new UAs */ If a URL uses a fragment identifier syntax that the implementation does not understand, or does not consider valid for that type of image, the URL must be treated as representing an invalid image Note: This error-handling is limited to image() and not in the definition of URL, for legacy compat reasons. 2.5.3. Solid-color Images If the image() function is specified with only a argument (no URL), it represents a solid-color image of the specified color with no natural dimensions For example, one can use this as a simple way to "tint" a background image, by overlaying a partially-transparent color over the top of the other image: background-image image rgba 255 .5 )), url "bg-image.png" ); background-color does not work for this, as the solid color it generates always lies beneath all the background images. 2.5.4. Bidi-sensitive Images Before listing any s the author may specify a directionality for the image, similar to adding a dir attribute to an element in HTML. If a directional image is used on or in an element with opposite direction the image must be flipped in the inline direction (as if it was transformed by, e.g., scaleX -1 , if the inline direction is the X axis). Note: Absent this declaration, images default to no directionality at all, and thus don’t care about the directionality of the surrounding element. A list may use an arrow for a bullet that points into the content. If the list can contain both LTR and RTL text, though, the bullet may be on the left or the right, and an image designed to point into the text on one side will point out of the text on the other side. This can be fixed with code like: ul style "list-style-image: image(ltr 'arrow.png');" li dir 'ltr' My bullet is on the left!
li li dir 'rtl' MY BULLET IS ON THE RIGHT!
li
ul This should render something like: ⇒ My bullet is on the left! !THGIR EHT NO SI TELLUB YM ⇐ In LTR list items, the image will be used as-is. In the RTL list items, however, it will be flipped in the inline direction, so it still points into the content. 2.6. Combining images: the cross-fade() notation When transitioning between images, CSS requires a way to explicitly refer to the intermediate image that is a combination of the start and end images. This is accomplished with the cross-fade() function, which indicates the two images to be combined and how far along in the transition the combination is. Note: Authors can also use the cross-fade() function for many simple image manipulations, such as tinting an image with a solid color or highlighting a particular area of the page by combining an image with a radial gradient. The syntax for cross-fade() is defined as: cross-fade () cross-fade && 100 The function represents an image generated by combining one or more images. The represents how much of each image is retained when it is blended with the other images. The must be between 0% and 100% inclusive; any other value is invalid. If any percentages are omitted, all the specified percentages are summed together and subtracted from 100% the result is floored at 0% then divided equally between all images with omitted percentages at computed-value time. While this is not reflected in the computed value, when all the arguments’ percentages sum to greater than 100% the sizing/painting details effectively rescale them so that they sum to exactly 100% On the other hand, when the sum is less than 100% the sizing/painting details effectively act like there’s an additional transparent argument, with its percentage set to the remaining value necessary to make the sum equal 100% If a is provided, it represents a solid-color image with “automatic” dimensions (it doesn’t participate in the sizing of the result image at all; see details in the sizing details below). Tests cross-fade-basic.html (live test) (source) cross-fade-computed-value.html (live test) (source) cross-fade-legacy-crash.html (live test) (source) cross-fade-legacy-2-crash.html (live test) (source) cross-fade-natural-size.html (live test) (source) cross-fade-premultiplied-alpha.html (live test) (source) cross-fade-target-alpha.html (live test) (source) 2.6.1. cross-fade() Sizing The dimensions of the image represented by a cross-fade() are a weighted average of dimensions of the arguments to the function; the arguments have no effect. They are calculated as follows: To determine the natural dimensions of a cross-fade() Normalize mix percentages from the function’s arguments, and let args and leftover be the result. If leftover is 100%, return no natural dimensions Let images be an empty list. For each argument of the function’s arguments: If argument is not an or is an with no natural dimensions, continue Let item be a tuple consisting of a width, a height, and a percentage. Run the object size negotiation algorithm for the as appropriate for the context in which the cross-fade() appears, and set item ’s width and height to the width and height of the resulting concrete object size Set item ’s percentage to the argument ’s percentage. If images is empty, return no natural dimensions Return a natural width and natural height that are weighted averages of the width and height of each item in images according to their corresponding percentages. Note: The percentages might sum to a value less than 100%, so a naive weighted-averaging process might need to normalize them first. 2.6.2. cross-fade() Painting The image represented by a cross-fade() is a weighted average of the input arguments to the function, calculated as follows: To determine the appearance of a cross-fade() Normalize mix percentages from the function’s arguments, and let args and leftover be the result. Let images be an empty list. Let size be a tuple of width and height, initialized to the result of finding the concrete object size of the cross-fade() function (using the natural dimensions of a cross-fade() ). For each argument of the cross-fade() function: Let item be a tuple consisting of an image and a percentage. If argument has an rescale it to size ’s width and height and set item ’s image to the result. Otherwise, argument has a set item ’s image to a solid-color image of the with size ’s dimensions. Set item ’s percentage to the argument ’s percentage. If leftover is greater than 0% append a tuple to images consisting of a solid-color transparent-black image with size ’s dimensions, and a percentage equal to leftover Let final image be an image with size ’s dimensions, and every pixel being the weighted linear average of the corresponding pixels of each item ’s image in images weighted according to the item ’s percentage. (Average both the color channels and the alpha channel of the pixels.) For the purpose of this calculation, each pixel’s color must be in pre-multiplied sRGB. Details on the above operation This is applying an N-way Porter-Duff dissolve operation to the source images. Wikipedia defines dissolve as a stochastic operation, with the result pixels independently randomly chosen from the source images’ corresponding pixels according to their source images’ weights, but as pixels shrink to infinitely small, this converges to doing color-averaging in pre-multiplied color space. In particular, this means that `cross-fade(white 50%, transparent 50%)` will produce a partially-transparent solid white image. (Rather than a partially-transparent gray, which is what you’d get if you averaged the opaque white and transparent black pixels in non-premultiplied space.) As converting to pre-multiplied does entail some loss of precision, and graphics libraries may or may not support this operation natively, as per usual any method can be used so long as it achieves the specified effect. For example, one can instead rebalance the percentages according to the alphas of each pixel, then do the color-channel averages in non-premultiplied space. E.g., to render cross-fade(rgb(255 0 0 / 1) 40%, rgb(0 255 0 / .5) 20%, rgb(0 0 255 / 0) 40%) rebalancing the percentages according to the 1 / .5 / 0 alphas would produce 40% / 10% / 0% (which renormalizes to 80% / 20% / 0%), at which point you can average the raw color channel values and end up with an rgb(204 51 0 / .5) image. (Note that the alpha channel is still averaged using the original percentages, not the rebalanced ones.) Return final image 2.6.3. Simplifying Complex cross-fade() Per WG resolution, define a notion of "equality" for images, and combine "same" images at computed-value time, summing their percentages. Per WG resolution, simplify directly-nested cross-fade() at computed-value time by just distributing the percentage and flattening; cross-fade(A 10%, cross-fade(B 30%, C 70%) 90%) becomes cross-fade(A 10%, B 27%, C 63%) 2.7. Using Elements as Images: the element() notation The element() function allows an author to use an element in the document as an image. As the referenced element changes appearance, the image changes as well. This can be used, for example, to create live previews of the next/previous slide in a slideshow, or to reference a canvas element for a fancy generated gradient or even an animated background. Note: The element() function only reproduces the appearance of the referenced element, not the actual content and its structure. Authors should only use this for decorative purposes, and must not use element() to reproduce an element with significant content across the page. Instead, just insert multiple copies of the element into the document. The syntax for element() is: element () element where is an ID selector [SELECT] Do we need to be able to refer to elements in external documents (such as SVG paint servers)? Or is it enough to just use url() for this? This name conflicts with a somewhat similar function in GCPM. This needs to be resolved somehow. Want the ability to do "reflections" of an element, either as a background-image on the element or in a pseudo-element. This needs to be specially-handled to avoid triggering the cycle-detection. When we have overflow:paged, how can we address a single page in the view? The element() function references the element matched by its argument. The ID is first looked up in the elementSources map, as described in that section. If it’s not found, it’s then matched against the document. If multiple elements are matched, the function references the first such element. The image represented by the element() function can vary based on whether the element is visible in the document: an element that is rendered is not a descendant of a replaced element, and generates a stacking context The function represents an image with its natural size equal to the decorated bounding box of the referenced element: for an element rendered using a CSS rendering model, the decorated bounding box is the smallest axis-aligned rectangle that contains the border image areas of all the fragments of the principal box for an element rendered using the SVG rendering model, the decorated bounding box is defined by SVG Note: Because images clip anything outside their bounds by default, this means that decorations that extend outside the decorated bounding box like box shadows, may be clipped. The image is constructed by rendering the referenced element and its descendants (at the same size that they would be in the document) over an infinite transparent canvas, positioned so that the edges of the decorated bounding box are flush with the edges of the image. Requiring some degree of stacking context on the element appears to be required for an efficient implementation. Do we need a full stacking context, or just a pseudo-stacking context? Should it need to be a stacking context normally, or can we just render it as a stacking context when rendering it to element()? If the referenced element has a transform applied to it or an ancestor, the transform must be ignored when rendering the element as an image. [CSS3-TRANSFORMS] If the referenced element is broken across pages, the element is displayed as if the page content areas were joined flush in the pagination direction, with pages' edges corresponding to the initial containing block’s start edge aligned. Elements broken across lines or columns are just rendered with their decorated bounding box Implementations may either re-use existing bitmap data generated for the referenced element or regenerate the display of the element to maximize quality at the image’s size (for example, if the implementation detects that the referenced element is an SVG fragment); in the latter case, the layout of the referenced element in the image must not be changed by the regeneration process. That is, the image must look identical to the referenced element, modulo rasterization quality. As a somewhat silly example, a element can be reused as a background elsewhere in the document: style src color white background lime width 300 px height 40 px position relative dst color black background element #src ); padding 20 px margin 20 px