8000 [svg] Support for passing CSS styles to an SVG used via img[src] · Issue #8634 · w3c/csswg-drafts · GitHub
[go: up one dir, main page]

Skip to content

[svg] Support for passing CSS styles to an SVG used via img[src] #8634

@brandonmcconnell

Description

@brandonmcconnell

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:

image

…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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0