-
Notifications
You must be signed in to change notification settings - Fork 747
Description
Table of Contents
Description
Currently, it is not possible to apply CSS styles to an SVG if it is used via img[src]
.
This proposal adds a new CSS syntax that would enable developers to target the root SVG element in such cases and apply CSS styles to it and its descendants.
Proposal
To achieve this, a pseudo-element can be added to the img src link that acts as the root for the SVG, allowing styles to be targeted for both the SVG and its child elements. The syntax for this can use ::src
as it is tied to the src
for the image.
Syntax
To target the root SVG element, the syntax can be as follows:
img::src {
/* CSS styles to be applied to the root SVG element */
}
To target child elements within the SVG, the syntax can follow standard CSS selector patterns:
img::src path {
/* CSS styles to be applied to a descendant element under the root SVG */
}
All usual CSS selector patterns, properties, rules, and other conventions would work here as if the SVG had been embedded directly on the page, under the img
element.
Architecture
Any styles applied to img::src
would not conflict with styles set on the img
itself. Rather, think of it almost as if the svg
element is a direct child of the img
element, if img
supported such a thing. This is very similar to how the ShadowDOM and shadow roots already work. In fact, the ShadowDOM may be the ideal implementation/solution for this proposal.
Consider this example:
img { border: 10px solid red; }
img::src { border: 10px solid blue; }
This above code would be treated similarly to a ShadowDOM tree where img::src
exposes the svg
element as a shadow-root under the image itself. So in this example, the two border
styles would not conflict but rather, the img
element would receive its assigned border
styles, and then the svg
would receive its border
styles applied via img::src
as if it had been assigned to the svg
element itself.
Examples
SVG source for below examples (expand/collapse)
For completeness, here is example HTML/SVG source that can be used with the below examples:
<!-- index.html -->
<img src="example.svg" alt="example image">
<!-- example.svg -->
<?xml version="1.0"?>
<svg width="250" height="150" style="border: 1px solid black;" xmlns="http://www.w3.org/2000/svg">
<g>
<rect width="36.416" height="36.416" x="30" y="30" fill="red" />
<text x="80" y="80" font-family="Verdana" font-size="24" fill="blue">hello world</text>
</g>
</svg>
This looks like this:

…and now for the actual examples:
To demonstrate the proposed syntax, here are two examples targeting the root SVG and a child element within it:
1️⃣ Example 1: Targeting the root SVG
In this example, the fill
property is applied to the root svg
element:
img::src {
fill: red;
border: 10px solid blue;
}
2️⃣ Example 2: Targeting a child element within the SVG
In this example, fill
and stroke
-related properties are applied to the rect
element, and font
-related properties are applied to the text
element within the SVG:
img::src g rect {
fill: violet;
stroke: red;
stroke-width: 3%;
stroke-dasharray: 2px;
}
img::src g text {
font-family: cursive;
font-size: 32px;
}
3️⃣ Example 3: Applying a rotation animation to the rect
element
In this example, an animation is applied to the rect
element within the g
group:
img::src g rect {
animation: rotate 3s linear infinite;
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
4️⃣ Example 4: Applying a transition to the text
element on :hover
In this example, a transition is applied to the fill
property of the text
element within the g
group. When the text
element is hovered, its fill
property is changed:
img::src g text {
transition: fill 0.5s ease-in-out;
}
img::src g text:hover {
fill: green;
}
5️⃣ Example 5: Styling all icons from a specific directory to be a default color and then change colors on `:hover` and `:active` states, respectively
In this more practical example, any svg
icons pulled from a specific directory (e.g. /my-icons/
) will receive a default color and then a different color and transition on hover, only when displayed under the .gallery
section:
:root {
--default-gallery-icon-color: cyan;
--hovered-gallery-icon-color: magenta;
}
.gallery img[src^="/my-icons/"]::src {
fill: var(--default-gallery-icon-color);
transition: all 0.5s ease-in-out;
}
.gallery img[src^="/my-icons/"]::src:hover {
fill: var(--hovered-gallery-icon-color);
transform: scale(1.25);
}
I'm not sure how feasible this example is, depending on if we can granularly pass user events like a hover state into the image's source. If so, this would be great.
Other thoughts & gotchas
In terms of security, I don't foresee too much if any risk in this feature. That said, I can imagine situations where someone may not want their SVG meddled with and would want a way to prevent style injections like this.
With that in mind, it could be helpful to add support for an attribute to svg
to enable disallowing style injection, like disallow-external-styles
:
<svg disallow-external-styles>...</svg>
Notably, while we could add an attribute like allow-external-styles
to instead allow such styling on a per-case basis, I think the better default would be to allow styling unless specifically disallowed.