1. Introduction
SVG is stylable with CSS, and when used inline in HTML, this capability can be very useful. For example, an SVG icon can take on a different color based on whether the user is hovering it or not, just by applying a :hover rule to it that changes the fill property.
When the SVG is referenced in a way that doesn’t allow selectors or CSS inheritance from the outer page to apply to it
(such as embedding it via img or iframe in HTML),
though, this functionality is lost.
The only way to change the display of such "external" SVG images
is to produce several of them,
and change which image you’re referencing.
This incurs delay on the page as a new resource is downloaded,
and disallows dynamic effects like CSS Transitions.
CSS link parameters are a way to set CSS custom environment variables on an "external" resource, either by a CSS property or thru a special fragment scheme on the URL. This gives a limited, but powerful, subset of the customizability that "inline" SVG images have to "external" SVG images, or to other linked resources that use CSS to style themselves.
A link parameter is a pair of a <dashed-ident> name, and an arbitrary (possibly empty) <declaration-value> value.
< svg > < path fill = "env(--color, black)" d = "..." /> </ svg >
By default, it will fill its shape with black, as that’s the fallback color specified. But link parameters can customize the color in several ways:
< img src = "image.svg#:~:param(--color,green)" >
img{ link-parameters : param ( --color, green); }
.foo{ background-image : url ( "image.svg", param(--color, green )); }
2. Setting a Link Parameter
An external resource can be accompanied by a list of link parameters, a struct composed of:
-
a name, usually a <dashed-ident>
-
an optional type, a <css-type>
-
a value (potentially empty), a CSS value
There are three ways to specify a link parameter:
-
via the link-parameters property, which applies to the resource itself (if the element represents an external resource), and to all external resources used in CSS properties on the element
-
via a fragment directive directly in the URL of an external resource.
If specified in multiple of these ways, all of the link parameters are appended into a single list for the external resource, in the order:
-
the link-parameters property on the element, if relevant
-
the link parameter directives of the URL
-
the param() <url-modifier>s in url()
If multiple link parameters exist with the same name, the last one in the list is used.
How to access link parameters in the linked resource is defined in the next section, § 3 Using Link Parameters.
2.1. The param() Function
All three methods of setting link parameters share the same syntax for defining a parameter, using the param() function:
param() = param( <param-spec> , <declaration-value>? ) <param-spec> = color | accent-color | [ <dashed-ident> <css-type>? ]
Depending on the <param-spec>, the param() function defines a link parameter in slightly different ways:
- color
- accent-color
-
-
Name:
accent-color -
Value: The second argument, if specified. Otherwise, the value of accent-color on the element.
-
- <dashed-ident>
-
-
Name: the <dashed-ident>
-
Type: the <css-type>, if specified.
-
Value: The second argument, if specified. Otherwise, the value of the custom property with the specified name on the element.
-
That is, param(--foo) indicates that the link parameter is using the default value, drawing from the --foo custom property on the element.
On the other hand, param(--foo,) explicitly gives the link parameter an empty value.
This is similar to var(--foo) having no fallback, but var(--foo,) having an empty fallback.
If the link parameter has a type, the value (default or manually provided) is parsed as that type. If it fails to parse as the given type, the link parameter is invalid and ignored. (The param() defining it is not invalid.)
Note: Authors can specify fallbacks by repeating a single param() multiple times, like param(--foo <color>, blue), param(--foo <color>, xztheta(1 2 3)), which will use the (currently undefined) xztheta() color function if it’s supported and blue in user agents that don’t support that function, similar to specifying a property multiple times.
A param() function usually has an element context from the way it’s used. If present, the value uses that context (and the type, if present) to fully resolve itself to a used value before being passed into the linked resource. If the value needs an element context to fully resolve, but doesn’t have one, the link parameter is invalid and ignored.
Even without a type, arbitrary substitution functions will attempt to resolve in the value, and might need an element context (for example, var() and attr() both do).
Other values will be left unresolved without a type; for example, param(--foo, currentcolor) will pass the currentcolor keyword itself, without resolving it as a <color>, and thus doesn’t need an element context.
Similarly, if a param() doesn’t provide a second argument, and doesn’t have an element context, the link parameter is invalid and ignored. (Because the default draws on the value of a property on the element.)
2.2. For An Entire Element: the link-parameters property
| Name: | link-parameters |
|---|---|
| Value: | none | <param()># |
| Initial: | none |
| Applies to: | all elements and pseudo-elements |
| Inherited: | no |
| Percentages: | n/a |
| Computed value: | as specified |
| Canonical order: | per grammar |
| Animation type: | discrete |
The link-parameters property is one way to set link parameters
on the element itself
(if it is an element representing an external resource,
such as an HTML img or iframe),
and on all external CSS resources specified on the element
(such as background images, etc).
Its values are:
- none
-
No link parameters are specified.
- <param()>#
-
A list of one or more link parameters.
The param() functions used in link-parameters use the element they’re set on as the element context.
Note: If there are multiple images on the given element (content image and/or CSS images like backgrounds), they’ll all recieve all the values in link-parameters, even if they don’t use them. This is generally safe, as you need to use env() to access a link parameter in the linked resource, tho color and accent-color do have additional side-effects.
2.3. For A Single URL: the <url-modifier>
While link-parameters sets link parameters for every URL on an element, sometimes you want to set parameters for only one resource, or use the same resource multiple times on an element with different parameters. For this, you can use one or more param() functions directly in the <url> specifying the linked resource, as a <url-modifier>.
.foo{ background-image : url ( "http://example.com/image.svg" param(--color, var(--primary-color )) ); }
The param() functions used in a <url> use the element they’re set on as the element context.
2.4. Outside Of CSS: in the URL itself
Link parameters can be used outside of CSS, by embedding them directly into the URL as a fragment directive.
Note: Text directives are another example of fragment directives, letting you link to specific text somewhere in a document.
The link parameter directive is a fragment directive with the following syntax:
:~:param(...)
Where the ... represents any sequence of URL codepoints and/or percent-encoded bytes.
After URL-decoding, the link parameter directive is then parsed as a <param()>. If it fails to parse, the directive is ignored. Otherwise, it’s added to the embedded document’s list of link parameters, as specified in § 2.1 The param() Function.
http://example.com/image.svg#:~:param(--text-color,blue)”.
Note: Spaces, and some other characters that might be valid in CSS syntax,
are not technically valid in URLs.
In some contexts,
you might need to escape those characters to form a valid URL.
In most cases, though,
such as HTML’s a element or CSS’s url() function,
spaces are accepted and do not need to be escaped.
Multiple link parameters can be passed to an image
by appending multiple link parameter directives to the URL.
When combined, either with each other or with other "fragment identifiers",
each value is separated with an & character,
as in a URL’s query parameters.
http://example.com/image.svg#:~:param(--text-color,blue)¶m(--bg-color,white)”
to set both env(--text-color) and env(--bg-color).
If the URL is specified in a URL attribute on an element,
such as <img src="...">,
resolving the param() values
(as specified in § 2.1 The param() Function)
uses that element as the context.
Otherwise (for example, if navigated to directly in the URL bar)
there is no element context.
Using in your document
works as expected,
drawing from the img element’s color property.
Navigating directly to example.svg#:~:param(color)
will instead ignore the param(color),
as if it weren’t passed at all.
Similarly, param(color, light-dark(black, white)) requires an element context (to determine the color scheme), and would work the same as above.
On the other hand,
param(--foo, light-dark(black, white))
does not require an element context,
as the parameter is untyped.
The literal value light-dark(black, white) will be passed down to the linked resource,
without resolving in any way,
whether specified in or directly navigated to.
3. Using Link Parameters
When an external resource link has one or more link parameters specified, if the linked resource understands CSS (such as an SVG or HTML document), then those link parameters affect the style of the linked resource in certain ways, depending on the parameter’s name:
- color
-
The initial value of the color property on the linked resource is set to the value.
- accent-color
-
The initial value of the accent-color property on the linked resource is set to the value.
- <dashed-ident>
-
A custom environment variable is established on the linked resource, using the name and value.
< svg > < g style = "fill: env(--color);" > < path d = "..." /> </ g > </ svg >
Alternately, it could just rely on the currentcolor keyword, allowing the color to be passed with param(color, blue).
-
On each env() function, provide a fallback value, like fill: env(--color, blue).
-
If the env() is going to be used a lot, such that providing a fallback for each individual env() is troublesome, store the custom environment variable in a custom property on the root element with the default specified, like:
:root
{ --color : env ( --color, blue); } In this example, if --color is provided via a linked parameter, var(--color) will contain its value. If not, it will contain the default blue value. In either case, var(--color) can be used in the stylesheet unconditionally, secure in the knowledge that it will always have a value.
Is it useful to supply the "color" and "accent-color" values via an environment variable as well? This would probably just mean defining env(color)/etc on all documents, resolving to the initial value of color.
3.1. Tainting
A custom environment variable created by a link parameter is attr()-tainted if the param() that defined the link parameter was attr()-tainted.
3.2. Multiple Images With The Same URL
When an HTML document contains multiple img elements with the same src value,
it considers them all "the same image" in some respects.
Notably, animation timers
(animating a GIF or similar animated raster format,
or driving a CSS animation on an SVG)
are defined to match across all such elements,
even if they’re added to the document at different times.
User agents generally implement this by creating only a single image document,
shared by all the img elements.
This solves the animation problem,
and avoids wastefully creating duplicate documents
when a single image is repeated many times on a page
(such as in the infamous "Hamster Dance" website).
However, link parameters allow these separate images to be styled differently.
(As does color-scheme, which affects the image’s preferred color scheme,
and potentially the width/height of the img,
which would affect the width/height media features.)
User agents must paint images with the correct styling values in these cases.
To the extent that it’s unobservable,
they can still use a shared document;
for example, they can associate each img with its own bespoke list of painting commands
or cached rasterization
while still having them share an underlying SVG document.
Privacy Considerations
This specification introduces no new privacy considerations.
Security Considerations
This specification introduces a new way to pass information to a linked resource, potentially from a hostile source.
While no explicit handshake is established for this channel, the use of env() to use the information minimizes the chance that the linked resource can be surprised by the information. The only way for the page to be vulnerable is to somehow be using an unknown env() in their styles, which will just result in invalid properties by default, and be visible in the developer’s Dev Tools.
Any hostile information can also only affect individual CSS properties that the resource explicitly opts itself into.