Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide a way to pass theming colors (and potentially other info) into SVG images, via e.g. Tab's SVG Parameters spec #6988

Closed
dholbert opened this issue Jan 26, 2022 · 8 comments

Comments

@dholbert
Copy link
Member

dholbert commented Jan 26, 2022

@tabatkins has a "SVG Parameters" spec proposal https://tabatkins.github.io/specs/svg-params/ that I'd like to see turned into an official draft, probably under the purview of the CSSWG (since it'd be a way to make information available via CSS).

Here's the elevator pitch, copypasted from the Introduction section of the spec proposal:

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

SVG Parameters are a way to set CSS custom properties on an "external" SVG image, by passing them through 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.

Mozilla is interested in implementing this feature in Firefox, once there's a CSS spec that's got consensus and seems ready for shipping. Our primary use-case is to allow our toolbar icons (for add-ons as well as the standard back/forward/home/etc.) to use web standards to style themselves in accordance with the user's chosen browser-theme, beyond just "is it dark or light". We think there are good uses for this feature on the web in general, too -- anywhere where a web developer might have some SVG assets that are referenced as images (e.g. <img> or CSS backgrounds) and where theming/coloring may want to be customized or may evolve over time, this sort of feature might be a good way to set and customize those colors.

Note: one of the core principles of this approach is that it needs to have opt-in from both sides (the outer document as well as the SVG image) in order to avoid data-leakage & unexpected side effects. The spec accomplishes this by: (1) baking the parameter-values into the image URL itself (as part of the fragment identifier or the CSS url() function), and by (2) having the image explicitly use the passed-in values (if it cares about them) using an explicit & newly-defined param() syntax.

I'm filing this issue to get the conversation started, and hopefully to gain consensus around this being a useful feature worth-speccing, and to find either an existing spec or create a new spec for this to live in. (Note that there's already draft text, in Tab's above-linked github.io space)

@dholbert
Copy link
Member Author

(Eventually we should mark this Agenda+ and we can discuss this feature in a telecon, but perhaps it's worth waiting a bit and gathering asynchronous feedback/thoughts here on github first.)

@SebastianZ
Copy link
Contributor

Note that this wouldn't only be useful for SVGs (for which I am currently missing this feature a lot) but also HTMLs embedded in iframes. A potential use case for that is to pass theme colors from the embedding document.

Sebastian

@dholbert
Copy link
Member Author

dholbert commented Jan 31, 2022

It's interesting to think about generalizing this to html and to iframes, however:
(1) you can already sort-of solve this for html and iframes by simply communicating via postMessage and JS.
(2) if we nonetheless were to generalize this feature to support html and iframes, then that would complicate or prevent the usage of the spec's current #param() syntax for encoding this information in the fragment identifier, since the fragment identifier might change if e.g. someone clicks an in-page link to scroll to an anchor further down in the iframe (and that's not great if that inadvertent in-page navigation causes a bunch of color-variables to be nerfed or changed).

(You also can already polyfill this feature in an iframe, by simply having the outer document provide the appropriate #param syntax as a suffix on the iframe's URL, and then having the inner document use JS to parse its document.location.hash to discover the #param() values and pick out whatever data it needs. It's only SVG-as-an-image contexts where JS is forbidden and so we need some sort of static way to automatically turn these parameters into CSS-exposed values.)

@dholbert
Copy link
Member Author

Put another way: I'm hesitant about scope-creep turning this proposal into a huge general-purpose scriptless technique for communicating arbitrary data from an outer context to an inner context (particularly in cases where this communication is already possible via other means).

I want to keep the focus on making this possible (and ideally ergonomic) for SVG images (where no such communication can be done at the moment). Having said that: if the feature lends itself to gracefully support other usages like iframes without much additional complexity/edge-cases-that-need-considering, then that's fine; but consider me a bit skeptical. :)

@SebastianZ
Copy link
Contributor

That's fair enough.

Sebastian

@dholbert dholbert changed the title Provide a way to pass communicate theming colors (and potentially other info) into SVG images, via e.g. Tab's SVG Parameters spec Provide a way to pass theming colors (and potentially other info) into SVG images, via e.g. Tab's SVG Parameters spec May 18, 2022
@astearns astearns added this to Unsorted in 2022 New York Meeting Jul 26, 2022
@astearns astearns moved this from Unsorted to Tuesday in 2022 New York Meeting Jul 26, 2022
@dholbert
Copy link
Member Author

dholbert commented Aug 2, 2022

For discussion purposes, here's some "prior art" in this neighborhood, i.e. other places where we've got SVG/icons that can be customized externally at the embedding site:

(1) Safari's "pinned tab icons", e.g. <link rel="mask-icon" href="website_icon.svg" color="red">

  • It's one way to pass a custom color into an SVG icon.
  • ...but in an extremely limited context (and via a non-standard mechanism).

(2) "icon fonts" that are monochrome & let you customize the icon colors just by literally setting CSS color.

  • It's one way to define a reusable icon-set which can be arbitrarily colorized.
  • ...but only supports customization of a single color (via CSS 'color') and requires you to package up your icons as a font.

(3) Inline-SVG icon toolkits, with fill/stroke being customizable at the embedding site.

  • This gives us nearly the flexibility that we want...
  • ...except that it uses inline SVG injected directly into the DOM. So this can't be used as a background or as an img element, and it accordingly requires a bit more trust, and doesn't benefit as directly from SVG-image rasterization caching benefits ("surface cache" in Firefox).

(4) SVG image files, referenced as images, with a viewbox specified in the fragment identifier, e.g. <img src="foo.svg#svgView(...)">

  • This is one way to do "SVG Sprites". It doesn't directly address the use-cases discussed here, but it's an example of how you can already use fragment identifiers to make an SVG image render differently (with cooperation from the SVG, e.g. by placing different components/icons at different predefined locations in the coordinate space).
  • This is similar but doesn't give us the flexibility that we're looking for here (passing in values like colors of the brand/theme).

(5) SVG images that render differently in dark mode, per #7213.

  • This gives you one bit of information that the image can react to (i.e. it could have a dark version and a light version)
  • ...but designers may need to enable more fine-grained customizability than that, and may want to e.g. vary the colors on hover, etc.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed SVG Parameters spec, and agreed to the following:

  • RESOLVED: Adopt SVG params spec as ED
The full IRC log of that discussion <astearns> topic: SVG Parameters spec
<astearns> github: https://github.com//issues/6988
<una> dholbert: proposed feature to allow SVG images to be customized using CSS
<dbaron> Scribe+ una
<una> dholbert: suppose you have a btn with an SVG image as its bg, and you want to be able to dynamically make it look different when its hovered/disabled/etc. or theme style
<una> dholbert: the only way to do that now is to have several different copies of the SVG image
<una> dholbert: theres no way to restyle an SVG image based on state
<una> dholbert: Tab has a proposed spec via letting you specify CSS variables in-url as a part of the fragment identifier as a part of the SVG image's URL
<bramus> Spec URL: https://tabatkins.github.io/specs/svg-params/
<una> dholbert: basic idea is in the fragment identifier you can specify the name of a CSS variable that will be exposed inside the SVG image
<una> dholbert: you can set the initial value inside the SVG image
<una> dholbert: normally css variables have no initial value, this changes that
<una> dholbert: so the embedding document sets the initial value and embedding file can observe or change it
<una> dholbert: if you're looking at the SVG file directly there is default
<una> dholbert: use case = icons in Firefox toolbar
<una> dholbert: we would like to be able to use it for icons of Firefox add-ons (to be able to hover them and have them respond to themes)
<una> dholbert: right now our proprietary version we have for our own iconography is turned off bc we dont want add-ons to create icons
<una> dholbert: we want to advance Tab's spec or something else if there are concerns
<una> TabAtkins: In favor of adopting, but discussed interaction with @Property's initial value, this will probably override
<smfr> q+
<emilio> q+
<fremy> q+
<astearns> ack smfr
<una> smfr: sounds similar to related discussion around allowing dark and light mode popping into SVG
<una> smfr: sounds like this will work in SVG for both image and iframe
<una> smfr: in iframes, SVG can exfiltrate this data that's pased in, but may be ok bc its uner c ontrol of parent frame
<una> dholbert: one concern about cross-origin iframes
<una> dholbert: worried about scenario where you have content that uses background property, expecting a color to be passed in, and the surrounding content passes an attacker control image (?)
<una> dholbert: for that reason, i think we should avoid cross-origin iframe usage
<una> smfr: agree - slightly concerning that an SVG could be made to load an external resource that could be part of a security exploit
<fantasai> smfr: so I agree with limiting to same-origin
<una> dholbert: if you're using it in an iframe, there's already ways to do the same thing - if we spec this for images you would also expect it to work similarly in an iframe
<astearns> ack emilio
<una> emilio: not sure about whether it should override @Property initial value
<una> emilio: that has sensible behavior, using CSS-wide keywords
<una> emilio: I'm not sure I'm a fan of passing stuff via the URL
<una> emilio: i.e. if you have a bunch of icons, you can have a single place where you define the props you're passing down to them
<una> emilio: you can put all your values in a CSS variable and pass those down, but I would have preference to having it be a CSS prop on the embedder element instead of as a part of the URL
<una> TabAtkins: objection to that is it works fine on an image or iframe element, but its less likely to work well if you're specifying CSS background-images that expect property names to be passed
<una> emilio: you cant use it in different URL values (cant pass different params to different URL values in the same element)
<una> emilio: may not have come across that use case but I do believe its important
<emeyer> q+
<una> TabAtkins: I'd be fine with both if thats okay
<una> emilio: feels like a bit of an abuse of the URL to stash things in the URL
<astearns> ack fremy
<una> fremy: I actually liked the oposing argument initially (im not sure why we want to restrict this to SVGs)
<fantasai> +1 fremy
<una> fremy: I think people use variables assuming they ?? - if we are changing the opt-in, we're changing it from something author controls to something URL controls
<dbaron> s/??/are controlled by the page/
<una> fremy: initial value if you specify a keyword from a param or a URL, if that is specified, that would enabel the mechanism to use the externally supplied value - 1st benefit is that its an opt in
<una> fremy: you explicity say this property is a color (for example) and you only get a color as a result
<una> fremy: you can protect yourself against invalid syntax
<emilio> q+
<una> TabAtkins: the premise that this opens you up for attack bc this changes your expectations for what syntax of a variable value will be is invalid usage and will screw up your property
<una> TabAtkins: in that case, it would make something valid that you would rely on being invalid, but not int he normal case
<miriam> q+
<una> fremy: you still use the value and assume that itll never cause an issue
<una> fremy: so thats a problem
<fantasai> s/still use the value/might be relying on the initial value/
<una> TabAtkins: i suppose thats true, i was trying to avoid a double handshake bc its more work, but i'm not opposed to it if it makes the cross-origins concerns less
<fantasai> fremy++
<una> q?
<astearns> ack emeyer
<dbaron> (I think fremy was talking about var() functions that provide a fallback value.)
<una> emeyer: as an author, i've run into the situation more than once that I've had to inline SVGs to do this same thing
<una> emeyer: makes sense for performance reaons if you want to refer to an SVG only once
<fantasai> s/initial/initial or fallback/
<fremy> (what @dbaron clarified is correct, I was mentioning authors using var(--non-existent, default) and hitting default right now)
<una> emeyer: have mashed my teeth before about wanting to adjust the fill, as an author i would be very interested in having a CSS property way of saying "this is what I want to pass into this resource"
<una> emeyer: i.e. im only transitioning <color> or <opacity> for this SVG -- these are the properties I want to pass in
<una> emeyer: would be super useful for that
<una> emeyer: however doesnt help with background SVGs and there needs to b e a way to address that as well
<una> emeyer: very useful to be able to say what properties i want to pass (ie. `pass-property: all`)
<una> TabAtkins: I propose we address emilio's and other concerns by saying we add a property that takes custom element names and potentially values to pass to the resource and any images used by the resource but also use the existing syntax in the spec
<una> TabAtkins: you wouldn't be able to flag non-custom properties
<una> TabAtkins: the values of arbitrary properties aren't sufficiently defined
<una> TabAtkins: i.e. if you wanted to pass stroke value you have to define it
<una> emeyer: as an author, that's a bummer but i understand the limitations
<una> emilio: do you mean completely override?
<una> TabAtkins: same name override
<astearns> ack emilio
<astearns> q+
<una> emilio: when fremy was talking about typing things, nothing stops you from doing that
<una> emilio: just bc they get validated at computed value time, doesn't mean you cant type it
<astearns> ack miriam
<una> miriam: not understanding cross-origin security concerns
<emilio> q+
<una> miriam: it seems to me custom properties are already considered insecure bc as a user i can change them
<ydaniv> q+
<una> miriam: already dont allow custom props to be used in a way that can be a security issue
<astearns> ack fantasai
<Zakim> fantasai, you wanted to react to miriam
<una> fantasai: the example that was given is: youre expecting to get a background color, someone passes a URL to an image they control, now its loading in your context with your permissions
<una> dholbert: the part thats not possible is suppose this is embedded content from a secure site, they specify a bg thats different if you're logged in or not, have a css property if you're logged in, an attacker can identify if you're logged in if updating the property sends them a ping
<una> astearns: i seem to remember we have extensions to the URL function and things we want to add to it in the future - do these play well ?
<una> TabAtkins: yes, it does
<una> astearns: would we need to add keywords?
<astearns> ack astearns
<una> TabAtkins: if we're doing a different type of parameter we can add keyword to express that
<una> emilio: depending on whether we make this handshake or not, you can already pass a lot of data in the URL hash in the iframe
<una> emilio: you could already right now use a custom property that isn't defined as such
<una> emilio: I don't think its the most concerning of risks
<una> emilio: on an iframe you can already script read the hash out and polyfill this for iframes right now
<una> emilio: we'd need to evaluate the risk, how many cross-origin pages use custom props in a way that could be dangerous
<una> emilio: if we double handshake it should be fair
<lea> q+
<una> emilio: still on the fence passing it in the URL hash vs. CSS function
<una> emilio: URL hashes have meaning right now
<una> TabAtkins: SVG syntax defines other thigns i.e. viewbox and media fragments in SVG hash
<una> emilio: if there is precedent, it makes me more comfortable with it
<bramus> s/thigns/things/
<una> TabAtkins: for it to be a problem you'd have to have a property with param
<dholbert> emilio: see https://css-tricks.com/svg-fragment-identifiers-work/#aa-syntax-for-html
<astearns> ack ydaniv
<una> ydaniv: still trying to understand usecase of loading an image: the concern is if you specify the URL for an image it will be loaded by that image instead of an expected color
<fantasai> dholbert: in an iframe, not a problem for svg image
<una> fremy: if you assume there's a security bug in the browser triggered by running a resource you'll still manage to trigger it
<una> dholbert: any time providing external data into a document you have this potential issue
<astearns> ack lea
<una> lea: 2 things - if we end up requiring a double handshake, can we jsut require it on cross-origin images and not everywhere?
<una> lea: i think its important to find use cases of cross-origin images
<emilio> q+
<fremy> +1 on lea's point, CDNs are popular
<una> lea: also - we need to make sure different uses of hash can be combined
<flackr> Just have to establish that it's uncredentialed right?
<una> lea: hash is used for many things today
<una> lea: need to preserve these capabilities, and in the future we might be using hash for even more different things - something to consier
<una> chris: we already use viewbox and you can combine it with other hashes
<una> TabAtkins: potentially only requiring double handshake for cross-origin is what i wanted to aim for as well
<una> TabAtkins: your other point about cross-origin images - i definitely agree we should be able to adress cross origin svgs in some way
<una> TabAtkins: uncredentialed
<una> emilio: cross-origin SVGs arent the issue bc they can't do a lot of things, its cross-origin iframes
<una> emilio: the problem is loading an SVG iframe not an SVG image bc its a different security model
<astearns> ack fantasai
<emilio> q-
<una> fantasai: if its loaded as an SVG image its not a security issue -- if its loaded as an SVG iframe the security model is more relaxed and we'd have to have some cross-origin security
<una> fantasai: emilio made a point that passing all this via URLs isnt great bc you have to repeat it every time for each image
<una> fantasai: you arent able to have shared information across the URLs
<fantasai> s/URLs/multiple images
<una> fantasai: the other bit of feedback was fremy - if we're doing this in a URL mechanism why are we restricting it to SVGs, why not expand it?
<una> fantasai: do we want to design a syntax that can we used i other formats as well
<una> fantasai: i.e. non-CSS resources
<una> TabAtkins: this is a CSS feature, don't care about other resources
<ydaniv> <object> also problematic
<una> fantasai: suppose word documents want to be able to interpret URLs and support media fragment syntax
<una> fantasai: discussed limiting passing of parameters in cross-origin context when loaded as an iframe but not worrying about that if loaded as an SVG image
<dholbert> ydaniv: <object> is effectively a synonym for <iframe> from a capabilities/security-model perspective, IIUC
<astearns> q?
<una> fantasai: emilio brought up issue if you're passing the parameter and setting the param vs. changing the inital value of the property in that document which are very different
<TabAtkins> yeah when we say "iframe" we generally mean "any of the elements that can load an active document"
<una> fantasai: fremy brought up issue that the image should be recieving this info and be able to or required to type the incoming prop data
<lea> it's important that it sets the initial value, not just setting the property, so SVG can provide fallback values via var(--prop, fallback)
<una> fantasai: so if you're trying to pass an image to a var thats expecting a color itll fail
<una> astearns: you want us to adopt this as an editors draft?
<una> dholbert: yes
<una> dholbert: at a high-level, it seems nobody is objecting to the overall idea but we should add property syntax to more concisely address list of passed in custom properties
<una> astearns: anyone have concerns and wants to address why we shouldnt adopt this?
<una> <silence>
<una> astearns: Proposed Resolution: Adopt this as an editors draft and start working on these issues
<fantasai> lea, I don't think that's an issue with setting initial value vs setting the value
<una> TabAtkins: i'm happy to do it
<una> TabAtkins: would like co-editor, dholbert volunteered as co-editor
<fantasai> lea, if the author defined the property with an initial value, they should get that value when the parameter is not set
<una> fremy: last thought i had was we have to consider use case of single-origin: What happens if you also use the address in the address bar?
<una> fremy: you could also introduce your variables in a website hyou don't control the origin
<fantasai> lea, the question is if the parameter overrides that initial value (and is represented by 'initial' keyword) or if it is just setting the property to a different value (like declaring it on root); I think the second is more appropriate
<una> TabAtkins: you could just load an SVG directly, we dont know if its single or cross origin
<una> TabAtkins: would consider that cross-origin for safety
<una> TabAtkins: if its an SVG we don't care as much, if its an iframe, choose safety
<TabAtkins> https://tabatkins.github.io/specs/svg-params/#using
<fantasai> RESOLVED: Adopt SVG params spec as ED
<dholbert> \o/

@dholbert
Copy link
Member Author

dholbert commented Aug 2, 2022

I filed https://bugzilla.mozilla.org/show_bug.cgi?id=1782815 on implementing this once we're ready to do so.

@dholbert dholbert closed this as completed Aug 2, 2022
tidoust added a commit to w3c/browser-specs that referenced this issue Sep 14, 2022
This used to be the "SVG Parameters" proposal. It got adopted by the CSS WG
early August, with implementation interest from Mozilla:
w3c/csswg-drafts#6988 (comment)

Fixes #705
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Tuesday
Development

No branches or pull requests

4 participants