Webcomponents: How should various document internal references work when SVG is being used in shadow DOM

Created on 6 Jul 2015  ·  81Comments  ·  Source: WICG/webcomponents

Title: [Shadow] How should various document internal references work when SVG is being used in shadow DOM (bugzilla: 27380)

Migrated from: https://www.w3.org/Bugs/Public/show_bug.cgi?id=27380


comment: 0
comment_url: https://www.w3.org/Bugs/Public/show_bug.cgi?id=27380#c0
_Olli Pettay_ wrote on 2014-11-20 12:37:34 +0000.

needs-clarification shadow-dom v1

Most helpful comment

I am working on a library of UI components for Angular. I use SVG icons declared on top of the page and referenced with use tag. When I tried to convert my library components to Web Components so no Angular is required I was very sad to find that it doesn't work with Shadow DOM. I understand the concerns, but practicality of this approach is just huge. This thought first mentioned way above by @devingfx :

I assume that is the same issue as inline <svg> symbol library :

I really hope this would be resolved.

All 81 comments

@smaug---- , if you have a change, could you elaborate this issue?

I think this is similar to the fragment identifier issue and we should resolve it in the same way. E.g., filter: url(#element-id) should not cross the shadow boundary.

I see. I think we can agree that it should not cross the shadow boundary.
If SVG spec is using a node tree correctly, I do not think we need any action.

So stuff like

<defs>
    <linearGradient id="Gradient01">
      <stop offset="20%" stop-color="#39F" />
      <stop offset="90%" stop-color="#F3F" />
    </linearGradient>
  </defs>
  <rect x="1cm" y="1cm" width="6cm" height="1cm" 
        fill="url(#Gradient01)"  />

won't be possible in shadow DOM. Doesn't that make it rather hard to use any real world SVG within Shadow DOM? (perhaps that is ok?)

But yes, I guess that is the same as filter: url(#element-id) issue.

As long as the SVG is inside the shadow tree, it will work, no?

It does? Then I've missed some spec change which makes that fragment identifier to work.

Perhaps the language in SVG assumes a document tree at the moment, but it seems reasonable for that to work in the same way that <form> can work when fully contained in a shadow tree, no?

Sure. But need to make sure the specs actually end up defining that.
(though, not sure I understand where in <form> handling there are random urls being used as id refs)

Ah, I asked about this here: http://www.w3.org/mid/[email protected]

Good to see this has already been discussed.

I assume that is the same issue as inline <svg> symbol library :

<!DOCTYPE html>
<html>
    <head>
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" 
             xmlns:xlink="http://www.w3.org/1999/xlink">
            <symbol id="icon-cog">
                <path d="...."/>
            </symbol>
            <symbol id="icon-close">
                <path d="...."/>
            </symbol>
        </svg>
    </head>
    <body>
        <custom-element>
            #shadow-root
                <svg xmlns="http://www.w3.org/2000/svg" 
                     xmlns:xlink="http://www.w3.org/1999/xlink">
                    <use xlink:href="#icon-cog"/>
                </svg>
                <svg xmlns="http://www.w3.org/2000/svg" 
                     xmlns:xlink="http://www.w3.org/1999/xlink">
                    <use xlink:href="#icon-close"/>
                </svg>
            /shadow-root
        </custom-element>
    </body>
</html>

I would say here that the fragment identifier should cross shadow boundary in this case.
By replacing the <svg> library by another one (loaded via xhr) following the same symbol naming,
you can swith he whole app icon set on the fly.
Theme authors can distribute several icons set this way, following the use-land library icon's namming
convention it's targetting.

The workaround to this if not crossing is to reference an external file like so:

<!DOCTYPE html>
<html>
    <head>
    </head>
    <body>
        <custom-element>
            #shadow-root
                <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"
                     onclick="root.toggleMenu( settingsMenu )">
                    <use xlink:href="icons/library.svg#icon-cog"/>
                </svg>
                <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"
                     onclick="window.close()">
                    <use xlink:href="icons/library.svg#icon-close"/>
                </svg>
            /shadow-root
        </custom-element>
    </body>
</html>

But if you need to change the <svg> library on the fly, you'll have to loop on each node to find shadow root to find nested <svg><use> and change the xlink:href manually, what can be a pain ...


I assume fragment identifier issue have to resolve the same way for all use cases, so maybe it's not possible to
specify that "fragment identifier don't cross boundaries, but not in case of embed svg"

I don't have recommendation here, I'm just pointing out a use case to take care of for this issue !

SVG use element is indeed an interesting use case to consider. We probably need to come up with a new syntax / feature to make it work across shadow boundaries.

With the advent of the implementations of Shadow DOM, this needs to be adressed: Devs who are trying to use the Shadow DOM cannot use non-trivial SVG at the same time, because Safari does not implement (or probably rather "actively disables") SVG Use elements inside shadow trees: https://bugs.webkit.org/show_bug.cgi?id=174977

They refer to this bug and say that it is because of the missing spec that they cannot implement svg use elements inside shadow doms.

I disagree, though and agree with @annevk that at least as long as the SVG in question is fully contained inside a shadow tree, it should work just like it would work at the root document level.
However because of the (missing clarification in the) spec, it does not work in Safari.

Could you please reconsider the priority of this bug? Because without a resolution web developers will either have to make the choice between not using Shadow Dom, not using SVG, and not using Safari. I am sure they will decide not to use Shadow Dom, which would be a pity.

  1. Regarding this:
    > as long as the SVG in question is fully contained inside a shadow tree, it should work just like it would work at the root document level.

I think everyone would agree that this should work. What clarification is required in SVG spec?

  1. Regarding this:
    > I would say here that the fragment identifier should cross shadow boundary in this case.

This is questionable. I object to it.

https://drafts.fxtf.org/filter-effects/#FilterProperty seems to define this property (though maybe it's defined elsewhere too?) and it's basically super vague.

I think everyone would agree that this should work. What clarification is required in SVG spec?

Unfortunately the Safari team disagrees here. Their implementation does not work for the "simple" case and they say it is because of the spec not being clear enough here.

Since filter property as well as use element use a URL to reference an element, I don't think we can make it work without defining how an element inside a shadow tree can be referenced using a URL.

Does any browser actually support full URLs there or just local fragment references? I thought it is mostly the latter, but if it isn't this is indeed more complicated.

Firefox definitely supports referencing a fragment in another file although Safari & Chrome don't support that.

@rniwa - while I agree that it is not defined how it should work (and I also believe that probably the best solution would be to not make it work at all) to reference a fragment inside another document buried in a shadow dom or inside another shadow tree in the same dom, my concern right now is that the Safari/Webkit team claims that the SVG spec is not even clear enough about local fragments inside the same shadow tree dom. Please correct me (and them) if I'm wrong, but IMHO at least this should work, or shouldn't it:

anywhere on the page (be it inside a closed, or open shadow dom tree or at the top-level) this must work:

<svg width="10cm" height="3cm" viewBox="0 0 100 30" version="1.1"
     xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <rect id="MyRect" width="60" height="10"/>
  </defs>
  <use x="20" y="10" xlink:href="#MyRect" />
</svg>

(slightly stripped down version from here https://www.w3.org/TR/SVG/struct.html#UseElement )

However according to them "because of the unclear spec" this svg does _not_ work inside Safari in a shadow DOM.

This makes all of our SVGs break if they are used inside a shadow dom in Safari, and thus all of our customers cannot use shadow dom in their products if they need to target Safari and I would greatly appreciate it if you could somehow make a clear statement that at least _this_ use-case must work according to the spec so that they will adjust their current implementation.

Thank you!

My concern right now is that the Safari/Webkit team claims that the SVG spec is not even clear enough about local fragments inside the same shadow tree dom. Please correct me (and them) if I'm wrong, but IMHO at least this should work, or shouldn't it:

You keep saying that this is a claim but it's not a claim. It's a fact that there is no spec at W3C that either defines or mandates the behavior you're describing. The fact Blink behaves in the way you'd like is almost a coincidence.

anywhere on the page (be it inside a closed, or open shadow dom tree or at the top-level) this must work

I'm not at all certain that everyone would agree to that proposition. xml:href in SVG use element uses an URL and an URL has a very well defined semantics. To make the behavior you're describing work, we'd have to override its semantics so that #foo references another node within the same shadow tree with the ID value of foo. If we're making this change for all fragment URLs then that would have serious implications on anchor elements and any other SVG and HTML elements that use href and xml:href attributes. On the other hand, introducing an arbitrary inconsistency like making this semantics change only to SVG use element is also bad for the Web platform as the whole.

OK, thanks a lot for the clarification. So I think it should be clearly communicated and stated that SVGs and shadow DOM don't work together nicely if implemented according to the current spec and that devs cannot rely on the implementations that currently do support this "against the spec". Even Anne van Kesteren thought that this feature should work according to the current spec if I interpret this comment correctly: https://github.com/w3c/webcomponents/issues/179#issuecomment-198231619 - without a clear statement how should the majority of "normal devs" understand the current situation?

SVG 'use' elements are a very important part for SVGs, at least if you use SVG to programmatically create vector graphics. So if they don't actually work in shadow DOMs that is a huge problem for shadow doms, IMHO: Shadow doms are used for creating and encapsulating complex web components. If those components cannot use sophisticated vector graphics, then this is a blocker for many component developers (like us) and they won't be adopting shadow doms, which would be a pity because they would benefit the most from shadow doms.

I understand that it may be difficult to _change_ the spec so that this will work formally, but I think that absolutely _needs_ to be done (and that's basically what this ticket is all about). It's almost as if you had decided not to support a rather basic feature like background colors or margins in shadow doms - people will choose to have background colors and margins over using shadow doms if that is the only way they can get background colors and margins to work. I say that if you want shadow dom to become widely adopted, this SVG use-case absolutely needs to be supported, whatever it takes.
Thank you!

The problem is that SVG uses URLs for both local and non-local references and most browsers only implement the local part. In CSS at least there was a plan to make these local reference special (URLs starting with #) and that probably applies here too given it's a CSS property. @tabatkins?

It seems that hasn't really propagated to all specifications yet.

Oh gosh, I didn't even think about the effect of scoped-IDs on frag-references. I guess it's the same issue as dealing with @font-face-defined names inside of a shadow tree.

So yeah, frag urls are special in url() - https://drafts.csswg.org/css-values/#local-urls. They stay fragments and are always treated as "local" to the referencing document no matter what the base url is.

It's a small hop from there to specifying that, within a shadow tree, they are scoped to referring only to the IDs defined in the shadow. I'm fine with that - is that how we want to proceed? If so, I can bring it to the CSSWG and get it approved.

@tabatkins yeah, that's basically the proposal. Search within your tree for IDs not within your document.

I don't think that solves SVG use element since it uses xml:href. Overriding its semantics inside a shadow tree is going to be quite ugly unless we're going to change it for everything else.

:-(

Better "ugly and working" than "nice and unusable", though, IMHO.

You mean xlink:href? It seems that has very similar considerations to CSS. Also, if we forget about shadow trees, would

<html:base href="https://elsewhere.com/"/>
<svg:use xlink:href="#test"/>
<svg:blah id="test">...</svg:blah>

end up finding svg:blah or not? If it does and the base URL gets ignored I think that would be a very strong argument for having similar semantics to CSS.

@grorg @hober

@annevk The base Url problematic with svg:use is a huge annoyance for devs in browsers other than Chrome (see https://gist.github.com/leonderijke/c5cf7c5b2e424c0061d2 and http://kb.yworks.com/article/706/ among others): In those UAs it does not resolve and thus breaks otherwise "self-contained" svgs. In Chrome, though the base URL is currently ignored at least for the svg:use use-case.

That behavior is so annoying (you need to replace all fragment urls with absolute urls which then again would not work inside shadow doms) - for me that alone would be a reason to adjust the spec.

Could we please raise the priority of this issue?

IMHO this issue is an absolute show-stopper for the adoption of webcomponents.

E.g.: We had to refactor our application that was using Polymer to basically get rid of all web components that used SVG. We had to pull those components to the document top-level and make them plain HTML instead of web components to make the application work in Safari. This worked for some components but was not feasible for others, where the functionality is still broken, now.

With the webcomponents shims the situation was actually better for us, but now that Safari has an implementation which does not allow using SVG with use elements the only workaround for devs is to drop web components.

Also I fear that our applications (which work great in Chrome right now) might cease to work in Chrome, too, once they decide to "stick to the spec" and cripple the (now working) SVG functionality in Chrome, too.

What if we just added a new content attribute for the use element? e.g. src or ref or from?

@rniwa can we first clarify https://github.com/w3c/webcomponents/issues/179#issuecomment-321477375? (I think the SVG WG is receptive to adding new attributes to replace namespaced ones, but we should first figure out how the existing setup works.)

cc @AmeliaBR

Thanks for looping me in, @annevk. Sorry I wasn't aware of this discussion sooner.

When talking about SVG and shadow DOM, it is important to distinguish two cases:

  • SVG <use> element shadow trees.
  • inline SVG within HTML/web components shadow trees.

Use-element trees already have some "magic" to them that isn't defined in the core shadow DOM specs, so they can have special behavior for URL references, too. For inline SVG in web components, I would expect that URL references would behave in a way consistent with other handling of URLs and ID references.

For the combination of the two (use element references inside inline SVG inside web components), I would hope that we can specify behavior in a way that naturally compounds through each shadow up to the document level.

SVG <use> element shadow trees

The SVG 2 spec includes my attempt to make sense of this situation, but note that this does not yet include a resolved change to make these trees "closed" mode, which will simplify some of the details, and hopefully be enough to get implementer commitments to actually work from the spec on other details. That said, until we actually get implementers working from the spec, all of this is subject to change based on feedback!

As far as URL references go, I would expect the advice currently in the spec to persist: as part of the <use> cloning, make all URLs absolute, relative to the base URL of the source document. With the benefit of a closed shadow tree, the implementation details can be left ambiguous, so it could instead be implemented by setting a <base> that applies within the shadow tree.

As a consequence of the absolute URL conversion, it doesn't matter if an SVG paint server or filter effect is included in the cloned document fragment (i.e., a child of the symbol versus a sibling): the reference will always be to the original element in the external file. Since the external file and the cloned copy are both read-only, there shouldn't be any rendering difference either way.

The current spec text only requires URLs to be made absolute when cloning content from external files.

For same-file clones, the rule is that "The shadow tree itself uses the same document base URL as the document that includes it." So URL references would be directly resolved against the original document tree. I think I chose this behavior based on spec or implementation testing of how links were handled in web components, but I didn't include a spec cross-reference, so I'm not sure.

In this case, the original element in the main document is mutable, but since the clone is required to be kept in sync with the original, there again should be no rendering difference in the case of graphical effects like filters or gradients, compared with referencing a clone in the local shadow tree.

(There may be some implementation complexity for properties where user agents currently only support same-page URL references for graphical effects. But the best solution there would be for the user agents to match the spec and support cross-file effects! 😉)

However, there is special behavior for animation element references. In this cases, the target element is something rendered on screen, not just abstract processing instructions. You need to know which clone or original to animate. To make interactive animations sensible, and consistent to how interactive CSS pseudoclasses are spec'd to behave, each clone can be animated independently. However, when animation is triggered by an event attached to an element id, it will be triggered "when any instance of an element with that ‘id’ receives the same event." This rule was designed to be consistent with event listeners, which in turn was designed to be consistent with cloned onEventX attributes.

(Note that the SVG WG has resolved to remove this section from this version of the spec, probably deferring it to the SVG Animations spec, so it should be considered less stable than the rest of the spec, and changes less urgent.)

In contrast, when handling ARIA ID references within use-element shadow trees, SVG-AAM currently recommends recursively searching out of the shadow tree for a match:

  • When processing WAI-ARIA attributes on elements within a shadow tree, the user agent MUST first attempt to match any IDREF values against other elements in the same shadow tree, before searching for a match in the host's node tree. If shadow trees are nested, the user agent MUST recursively search from the current shadow tree to the tree containing its host use element, until the document tree is reached.

  • In all other cases, when matching IDREF values in WAI-ARIA attributes, the user agent MUST NOT consider elements in the shadow tree of a use element.

That is still an open issue on the SVG-AAM spec, with the intention to harmonize the behavior across all types of shadow trees, see https://github.com/w3c/svg-aam/issues/7 and https://github.com/w3c/core-aam/issues/4 Note that for ARIA, these are same-document ID references, not URLs, so generating an absolute URL is not an option.

inline SVG within HTML/web components shadow trees

There's nothing in the SVG specs particular to this case. I would expect that URLs in this case would be resolved in a manner consistent with handling URLs and ID references in web components in general.

If the chosen approach is to treat a shadow tree simply as if it inherits the base URL of the document, then it would seem at first glance as if you couldn't create local references to elements that only exist in the shadow tree.

So in this structure, the #outer reference would work, but not the #inner reference:

<svg>
  <symbol id="outer">...</symbol>
</svg>
<custom-element>
  #shadow-root
    <svg>
      <use href="#outer"/>
      <symbol id="inner">...</symbol>
      <use href="#inner"/>
    </svg>
</custom-element>

However...

CSS Values and Units 4 defines special behavior for fragment-only URLs (defined by whether they start with a # character), so that they always resolve to an element in the current document, regardless of any <base> URL changes, and regardless of whether the CSS rule was declared in the current document or another one.

The SVG 2 guidance on identifying a target element was intended to create a similar effect, by dynamically comparing generated absolute URLs against the current base URL of the document to define whether it is a local or external reference.

Neither approach is well implemented yet, so both could be revised to create a definition of a "local" reference that takes shadow tree nesting into account. For example, to follow the algorithm that I proposed in SVG-AAM, and find the nearest-matching element by starting in the local shadow tree and then moving toward the light.

I would strongly urge that any such algorithm be defined in the shadow DOM specs in a way that applies to all cross references, both ID references and local URL references, in SVG, CSS, HTML (e.g., for attributes), or ARIA.

Thanks for the elaborate description, @AmeliaBR!

@annevk : Sorry, yes, I meant to say xlink:href in https://github.com/w3c/webcomponents/issues/179#issuecomment-321475640

Note that WebKit doesn't support href on SVG's use element so only xlink:href works (see https://webkit.org/b/153854).

I would not be comfortable changing the definition of xlink:href just for use element. On the other hand, I expect changing the behavior of xlink:href for all cases involving shadow DOM to be even more controversial and not web/enterprise-content compatible.

Given that, I'm inclined to say that we should introduce a new SVG content attribute for use element which resolves like CSS ID.

The question is how xlink:href works today. Does it special case values starting with # or does it parse the value against a base URL?

It is still unclear to me whether people want inside shadow DOM to be able to (A) refer test element in light DOM, or (B) test element in shadow DOM, or (C) both?

https://github.com/w3c/webcomponents/issues/179#issuecomment-392605203 seems to hint both, so that id reference could be either within a shadow DOM, or between shadow DOM and and ancestor shadow DOM, or shadow DOM and light DOM. That would be very strange and if one implements component and has on purpose unresolved idref there (for example temporarily while some script is building the idref target), such shadow DOM idref could magically refer outside of shadow DOM.
And also, other idrefs don't work that way.

http://mozilla.pettay.fi/moztests/svg_shadow.html
Chrome shows red circle in first one, Nightly in second one, and I was told Safari doesn't show a red circle at all.

Note that since <svg:use> is defined to use a shadow tree, if we special-case shadow trees it's going to probably behave unexpectedly for nested <use> elements, for example.

@smaug---- I would expect that if we treat those references starting with # in a special way, #test would only refer to elements within the same tree, so you cannot get into the light from a shadow, or into a shadow from the light. That's how ID references work elsewhere already.

And if one uses absolute url, there wouldn't be any magic? Apparently blink applies the magic also when absolute url (pointing to the document's url) + fragment is used. And that is rather weird. The idref explicitly points to an element in light DOM, yet shadow DOM is used.

I'm not sure, having magic there might be okay too, if that's how CSS does it. I do agree it'd be a little weird.

It is still unclear to me whether people want inside shadow DOM to be able to (A) refer test element in light DOM, or (B) test element in shadow DOM, or (C) both? (@smaug---- )

Both have their use cases (which is why I suggested the "search outwards" approach).

If I had to choose one or the other, though, I'd pick (B) for web components. If a web component contains a complete, self-contained inline SVG within its shadow DOM, cross references within the shadow tree need to work as normal. Anything else basically means no inline SVG inside web components.

But if hash references in the shadow tree always resolve inside that tree, there are some consequences:


Consequence 1:

If a web page author wants to use an SVG icon in their web component that references definitions in the main document (i.e., to link from shadow to light), they would need to refer to the main document by its full URL, and deal with any complications from <base> and location changes.

Alternatively, an <svg><use xlink:href="#icon" /></svg> fragment could be defined in the light tree, and pulled into the shadow tree using a <slot>. I would _assume_ that in this case, since the <use> was defined in the light tree, it's attributes would be evaluated in the light tree, similar to how I'd expect this jump link to still work even if I pulled it into a web component shadow widget:

<fancy-jump-link>
<a href="#section2">Next Section</a>
</fancy-jump-link>

Consequence 2:

I would expect that the same rules would apply to other (xlink:)href attributes inside the shadow DOM. So, if I wanted to generate a separate, completely shadow <a> element inside my <fancy-jump-link> that would link out to the main document, I would need to use the full document URL, including any base adjustments.

But, this may be a web compat problem of its own by now. I don't know.


Consequence 3:

We need to add some magic to <use> element shadow trees to get them to work, since nested <use> elements that were cloned from the light document need to be able to reference the light tree, to maintain web compatibility. Specifically:

  • If a use element references another element in the same document (<use id="a" xlink:href="#b"/>), and that second element or its child references a third element elsewhere in the document (<g id="b"><use xlink:href="#c" /></g>), the second reference needs to match up correctly to the element in the light document tree (<g id="c">...</g>).

The magic required to solve that is to make the URLs in the <use> element shadow tree absolute when the elements are cloned, or define a new base URL for the entire subtree. Which happens to be the magic already proposed in the spec for handling <use> elements cloned from other documents, so that's not too bad.

But xlink:href is the easy part, because it's an attribute and always attached to a specific element. So it's fairly straightforward to apply modifications to the value when the element is cloned.

The harder part will be the url(#effect) references in CSS, because CSS properties can inherit from light tree into shadow.

These also need to work for web compatibility:

  • If a use element references another element in the same document (<use id="a" xlink:href="#b"/>), and that element or its child references a graphical effect elsewhere in the document (<g id="b" filter="url(#blur)">...</g>), then the effect needs to be applied on the cloned element.

  • If a use element references another element in the same _or another_ document, and the use element has an inheritable graphical effect that references an element elsewhere in the original document (<use id="a" xlink:href="#b" fill="url(#pattern)" />), then the effect needs to be applied on relevant elements in the shadow tree.

Both of those are references from the shadow tree back into the light.

The following should also work (although current web compat is sketchy):

  • If a use element references another element in a different file (<use id="a" xlink:href="icons.svg#b"/>), and that (cloned) element references an effect elsewhere in its own file, that effect is applied.

Which means you need to keep track of whether the style property was defined in the light tree and inherited into the shadow, or whether it was defined in a different document and cloned into the shadow tree.

Keeping track of the source file is consistent to how CSS URLs have _traditionally_ been handled, but inconsistent with the new special CSS rules for hash-only URLs. It also means that you can't just think of the use-element shadow tree as having a separate base URL and have everything fall into place. The current SVG text says that the hash-only CSS URL defined in the other file would be made absolute relative to that file's address as part of the cloning process, so that once it gets to the shadow tree context it is no longer "hash-only". But that does create an extra complication for implementation.


The question is how xlink:href works today. Does it special case values starting with # or does it parse the value against a base URL? (@annevk)

For a use element in the light DOM, the xlink:href parses against the base URL. That is generally considered Not A Good Thing by web developers, as it breaks the page if you use <base>, but also if you push a new URL without using <base>. This is the same problem that prompted the new CSS rules (implemented in Chrome) for hash-only.

The current SVG 2 spec text was an attempt to work around this, by resolving the URL against the base URL, and then comparing it against the current base URL of the document to decide whether to treat it as a same-page reference or not. (But if that could be defined in a more authoritative spec, that would be an improvement, to me.)

Since the current spec text works with the base URL, it doesn't get any better or worse if we use base URLs to define shadow tree URL-resolution behavior. We'd just need to rewrite it to talk about the "tree" base URL, instead of the "document" base URL.


Note that since <svg:use> is defined to use a shadow tree, if we special-case shadow trees it's going to probably behave unexpectedly for nested <use> elements, for example. (@emilio)

Yes, indeed. We need to handle both cases, and that's not easy, which is why we're talking about adding "magic" to the use-cloning process to make it all fit together.

I'm generally of the opinion that:

  1. Fragids should be local to the shadow tree, like what HTML forms do.
  2. URLs should use CSS's rules, where fragment-only URLs are specially treated as always-local, and thus act like fragids and be scoped to the shadow tree.
  3. All other name-defining things (and the properties that reference them) should use my suggested resolution rules for name-defining CSS constructs, which we need to converge on for various CSS things anyway.

Ideally, tho, this would mean that SVG references would need to grow a CSS-ish reference syntax, so they could use scoped() too to refer to things defined up-tree. But that's something we can solve later.

http://mozilla.pettay.fi/moztests/svg_shadow.html
Chrome shows red circle in first one, Nightly in second one, and I was told Safari doesn't show a red circle at all.

FWIW, Nightly changed behavior here, and is now same as Chrome. And Safari resolves #ids within Shadow DOM in non-use element case https://bugzilla.mozilla.org/show_bug.cgi?id=1483882#c3

Hm... perhaps we should make this work just for href and not xlink:href. WebKit just added the support for href so we'd have less concerns for compatibility if we did that.

Hm... perhaps we should make this work just for href and not xlink:href.

I hope not! Authors are advised to keep using xlink:href for backwards compatibility reasons. And besides, both attributes map to the same DOM property. It would be very confusing if they behaved differently.

@AmeliaBR, clearly there is no compatibility risk for SVG use elements inside a shadow tree since it doesn't work at all in WebKit?? I'd be concerned about changing the semantics of xlink:href just in SVG use elements.

+1 for doing anything to make SVGs work - I have yet to see someone (a developer, not a spec writer) who prefers a standard conforming implementation where the standard says that it "must not work" over an implementation that actually works. I never thought I would say this, but I would even appreciate a new "workinghref" property that works!

-1 for not doing anything and waiting until either SVG or webcomponents or both are dead

That said - for us and our customers you did it: webcomponents are dead and we highly recommend against using them because SVG does not work. Sorry for the rant, but "years" is just too long to wait for a working implementation. Let's just hope that Angular, React and Vue and whatever will come next will never switch to webcomponent technology.

@yGuy Here's an implementation, it's not that hard to workaround this issue...

if (isNativeShadow && isWebKit) {
    svg.querySelectorAll('[*|href^="#"]').forEach(node => {
        const xlinkNS = 'http://www.w3.org/1999/xlink';

        let href;
        if (node.hasAttributeNS(null, 'href')) {
            href = node.getAttributeNS(null, 'href');
        } else {
            href = node.getAttributeNS(xlinkNS, 'href');
        }

        if (href === null || !href.startsWith('#')) {
            return;
        }

        const refNode = svg.getElementById(href.slice(1));
        if (!refNode) {
            return;
        }

        const refClone = refNode.cloneNode(true);
        refClone.removeAttribute('id');

        Array.from(node.attributes).forEach(attr => {
            if (attr.localName === 'href') {
                if (attr.namespaceURI === null || attr.namespaceURI === xlinkNS) {
                    return;
                }
            }
            refClone.setAttributeNodeNS(attr.cloneNode(true));
        });

        node.parentNode.replaceChild(refClone, node);
    });
}

@sergio91pt thanks, but this is not really helpful - it will only be water to the spec people's mills who think that the situation isn't actually as bad as it is, "now that we have a workaround". :-(

For everyone pointing to this workaround: This will not work in many cases: Don't try this on any dynamic, reasonably sized SVG. The performance is horrible, especially on iOS devices (which would require this workaround). It only works for small, static SVGs (which you could include as a regular, spec-conforming SVG image, too), but not for the dynamic use-case. And of course it does not work with nested 'use' elements, either. The easier and better workaround is to simply ditch webcomponents.

FYI, I tried gathering some author feedback via Twitter poll. Didn't get a lot of responses, but the expectation seems to be that <use href="#svg" clip-path="#web"/> should look for those references in the same shadow tree, possibly falling back to searching parent trees if there is no match in the shadow.

Web dev nerds! It's poll time again!

If you're using a shadow-DOM web component, and an element includes a # url reference, like:
<use href="#svg" clip-path="#web"/>

Where would you expect the browser to look for the element with that id value?

(IDs are only unique per tree)

  • Only in that shadow tree
    53%
  • Only in the main document
    12%
  • Search shadow, then main
    30%
  • Something else (reply)
    5%

43 votes · Final results

Thanks, @AmeliaBR - if you had announced the poll, here, I am positive you would have received more votes. My vote would have been for option 1, but option 3 is also acceptable. Only option 2 is an absolute no-go.

Let's hope this will get standardized and implemented (I'm looking at you, Safari), soon, before it's too late for webcomponents.

Thank you everyone for your help!

+1 for option 1. Basically, every non-absolute reference, like a #foo, should be local in a node tree. That's consistent with other features of Shadow DOM, and how DOM works in general. We should update the spec to reflect that.

Regarding implementation, Blink has already made idref be scoped to a local node tree (fixed a bug).

So it won't be possible to use symbol library in main document with web components using it in their shadow DOM's element? So what we will have to create a web component for each icon to be able to embed it shadow DOMs? Or create a DOM observer to automatically copy the hole library it each nested component's shadow ?
Such a loose of processor cycles for nothing!

The option 3 is a must have for web components to succed...

May I prupose Option 4 (Something else) :

Why not let the web dev choose by letting them configure the shadow DOM's frontier?

Something fine tuned like :

node.attashShadow({
    style: "closed",
    idref: "open",
    events: "document"
})

With all this shadow closing boundaries going to unsolvable problems, maybe the final answer is yes sometime, and no some other time, so with this fine tuning each case can be made depending on what's needed...

3 is definitely no-go. Would be rather annoying to track id changes both in document and shadow dom.

+1 for option 1, but I'd be ok to resolve only pure fragment references inside shadow DOM, and absolute reference could point to the light DOM. I think that is close to https://github.com/w3c/webcomponents/issues/179#issuecomment-410071854

@devingfx

So it won't be possible to use symbol library in main document with web components using it in their shadow DOM's element? So what we will have to create a web component for each icon to be able to embed it shadow DOMs? Or create a DOM observer to automatically copy the hole library it each nested component's shadow ?
Such a loose of processor cycles for nothing!

I think we need another primitives to address this use case, as we did it for style sharing in multiple shadow trees with Constructable Stylesheets.

We might want a similar primitive so that we can re-use a common SVG library in multiple shadow trees easily and cheaply.

On Constructable StyleSheet:
Is it intended to be able to adopt stylesheets from <style> tags or document.stylesheet (aka shared theme styles not component styles) ?
If yes, the same way shadow may gain a shadow.adoptSVGrefs( mySVGnode ) but it's still a pain for a final user (themer) to have to observe mutation and make each shadowDOM recursively to adopt a root theme/svg ...

On <svg> : maybe a new primitive ... But you won't make graphists or front-end dev rewrite their svg symbol library in javascript because of "Constructable SVGs" ^^; (like old dark times where we constructed DOM with loops of document.createElement() ... setAttributes ... appendChild )

As far as sharing SVG definitions between multiple web component instances, the best solution is to put the SVG content in a separate file. No additional primitive or API is needed, though browser support needs to improve. All current browsers support use-element references to other files (like <use xlink:href="icons.svg#home">), but only Firefox reliably supports references in SVG graphical effects properties (like <g clip-path="url(effects.svg#star)" filter="url(effects.svg#sparkle)">).

(The reason constructible stylesheets are necessary for external CSS files is because the CSS OM generated from them is modifiable. Currently, each shadow DOM tree might have its own, slightly modified version of that CSS OM that needs to be kept separate. In contrast, external SVG files are not accessible as a DOM from user scripts, and are certainly not modifiable, so there is no problem with the browser sharing the representation of those documents internally.)

This of course assumes that any URL reference that includes a file name (or more) would continue to be resolved relative to the base URL for the whole page. Only hash-only URLs would resolve inside the same shadow tree. A special behavior for hash-only URLs is also consistent with the latest spec (and some implementations) for resolving URLs in CSS stylesheets.

This would also mean that you could always reference back to the main document by including its own file name in the URL. Which isn't ideal, but it keeps the option available if necessary. As an author, I prefer the flexibility of the recursive-search approach (option 3 in my poll)—but I accept that it might be too much of an implementation headache that threatens performance.

Recall that back in my last comment I have a proposal for referring to things (both IDs and other global names) outside of your local shadow scope when you really want to.

Rough consensus at TPAC F2F: We would take Tab's proposal in https://github.com/w3c/webcomponents/issues/179#issuecomment-410071854 excluding (3) and have an explicit list of elements and attributes to which this behavior would apply since this would not make sense for things like href in anchor or area element.

(In particular, this probably applies to all URLs that do not end up navigating.)

For SVG this would mean <use> would reference the matching element within the current context of the use element - the Web Component in the example above. This does not apply to SVG’s <a> element which is navigational.

@rniwa Was it discussed if this new rule applies to href only or to xlink:href as well (which is defined as alias in SVG2 to href)? You were sceptical about allowing it on xlink:href.

@dirkschulze that was not discussed, but I think those should behave identically.

While I'm sad I missed the discussion (I had to head home Friday morning), I'm very happy y'all agreed with me and went with local-only. I wish y'all had been able to discuss it on Thursday when I could have been there, tho, because I could have explained why you actually can't exclude my part (3).

It's required to represent inherited values. Without it, for example, what happens when the outer page sets body { font-family: some-network-font; }, then the shadow DOM asks for the computed value of font-family on one of its elements? You can't say some-network-font, because that doesn't round-trip; the outer page's @font-face defining that font isn't visible to the shadow. (And the shadow might have defined its own @font-face of that name; the component shouldn't have to worry about name conflicts with the outer page!)

@TabAtkins,

I think we may have to tackle the issue of things represented as custom idents in CSS (like font family names) separately from discussing URLs. URLs already have a concept of absolute vs relative, and CSS values that contain URLs are already made absolute based on their source file.

URLs and inheritance are still an issue, of course, especially to keep SVG use-element shadow trees working in a backwards-compatible way. For example:

<use href="#icon" fill="url(#gradient-from-this-file)" />
<use href="other-file.svg#icon" fill="url(#gradient-from-this-file)" />

The gradient needs to apply on the cloned shapes in the shadow DOM (which inherit the fill property), even though it is defined in the outer page as a hash-only URL, and there is nothing in the shadow that matches that hash.

I'm not sure how Chrome & Firefox currently implement this to make it work for <use> elements, but if we want to generalize & make it work in any shadow tree, then hash-only URLs would need to be made absolute when inheriting across a shadow boundary.


PS, I just ran into this bug in a real-world project, where SVG text path inside a web component doesn't work in Safari! So I do hope that coming up with a solution for every situation doesn't delay basic support for local hash-references in shadow DOM.

CSS values that contain URLs are already made absolute based on their source file.

Not true if they're fragment-only; those are kept as fragments and are always a local reference. They'd be affected by this.

URLs and inheritance are still an issue, of course, especially to keep SVG use-element shadow trees working in a backwards-compatible way.

Yup, this is exactly the issue. The value needs a way to indicate which tree the reference is resolved against, when it inherits in from a higher shadow tree. Whether the referencing syntax is via an ident or a fragment doesn't change things.

then hash-only URLs would need to be made absolute when inheriting across a shadow boundary.

Absolutizing a URL doesn't fix anything. The ID being referenced doesn't have to be top-level.

Not true if they're fragment-only; those are kept as fragments and are always a local reference. They'd be affected by this.

Yes, of course. I just meant that _if_ we want to make something absolute, there is a syntax to represent it.

But yes, I wasn't thinking that absolute vs local hash isn't enough, because you can have nested shadow trees, and you still need to match the hash against the tree in which it was defined.

So, if a new syntax is needed for hash-URLs to define its tree scope then, yes, it makes sense to reuse the same syntax for other locally-scoped references in CSS.

(3) should be discussed orthogonally in https://github.com/w3c/csswg-drafts/issues/1995. This particular issue is concerned about SVG use element and other elements which use href. Let's not turn this into a mega issue which has a tendency of never reaching any consensus.

From what we discussed at TPAC, we can at least make SVG use element functional within a shadow tree, and there will be a well defined behaviors for other elements which use href.

This particular issue is concerned about SVG use element and other elements which use href.

It absolutely is not limited to that; many of the early posts in this thread explicitly mention SVG CSS properties that take url(), and which are inherited.

Inheriting a url() value should not cause a complete reinterpretation of the value (possibly causing it to resolve to a completely different reference); if you don't take (3) or something similar to it, however, that's precisely what will happen.

You can't push this issue off until later, as resolving it one way or another changes the fundamental behavior of commonly used properties.

It absolutely is not limited to that; many of the early posts in this thread explicitly mention SVG CSS properties that take url(), and which are inherited.

That is in the context of figuring out how href should work, not now url should work.

You can't push this issue off until later, as resolving it one way or another changes the fundamental behavior of commonly used properties.

If Google's position is that (3) is a must have, then we have no consensus to proceed with your proposal. We (Apple's WebKit team) would then have to retract our support for the proposal until such time is reached that a complete solution for https://github.com/w3c/csswg-drafts/issues/1995 is proposed and a consensus is reached. As far as I can tell, that's unwarranted delay of resolving the issue that SVG use element doesn't work at all in shadow trees. I'd rather resolve that issue first before resolving other CSS global namesspacing issue especially because that's a concern of CSS WG, not SVG or Web Platform WG.

Sigh. Brinkmanship isn't an attractive look.

I could go on, but no, trying to claim that this issue is just about href attributes is absolutely wrong. You are right that href attributes, because they don't inherit, don't need to worry about inheritance semantics. But this issue can't be resolved without talking about inheritance semantics, because most of it is talking about inheritable CSS properties.

That said, the current proposal to solve the non-inheriting href attributes by saying that they're scoped to the shadow tree they appear in is just fine. It's not affected by however we decided to handle inheritance semantics. There's absolutely no problem with resolving on these href semantics right now.

I'm just saying that we can't close this issue as resolved without talking about inheritance, because it's been about inheritance from the very beginning.

I'd rather resolve that issue first before resolving other CSS global namesspacing issue especially because that's a concern of CSS WG, not SVG or Web Platform WG.

While we generally consider these to be more CSS-ish these days, they're all present in plain, CSS-less SVG as well; <rect fill="url(#frag)" /> still needs to define how it works in shadows. This is a general problem with inheritable values; it's not scoped to CSS itself.

I'm just saying that we can't close this issue as resolved without talking about inheritance, because it's been about inheritance from the very beginning.

Okay, then why don't we create a new issue in which we just track href issue. This issue is then blocked by that issue as well as https://github.com/w3c/csswg-drafts/issues/1995

I am working on a library of UI components for Angular. I use SVG icons declared on top of the page and referenced with use tag. When I tried to convert my library components to Web Components so no Angular is required I was very sad to find that it doesn't work with Shadow DOM. I understand the concerns, but practicality of this approach is just huge. This thought first mentioned way above by @devingfx :

I assume that is the same issue as inline <svg> symbol library :

I really hope this would be resolved.

Rough consensus at TPAC F2F: We would take Tab's proposal in #179 (comment) excluding (3) and have an explicit list of elements and attributes to which this behavior would apply since this would not make sense for things like href in anchor or area element.

@rniwa Was there any discussion around behavior for fragment-only url values for anchor/area href attributes? Your comment seems to imply that these could potentially reference elements outside the shadow and I'm curious if this is the case.

@rniwa Was there any discussion around behavior for fragment-only url values for anchor/area href attributes? Your comment seems to imply that these could potentially reference elements outside the shadow and I'm curious if this is the case.

I don't think anyone thought allowing references to a node outside one's shadow tree was a good idea at least during F2F.

Don't know whether this is the right place to discuss this, but I think it is at least related - sorry for the noise if this is the wrong place:

The CanvasRenderingContext#filter property can also be used to reference filters declared in SVG and it also uses the url syntax to reference filters declared in SVG.

I would hope that it works like this: at least if the canvas which provided the canvascontext lives in the same shadow DOM as the SVG filter, a relative url should resolve to the filter in the SVG. If the canvas image is currently not in the DOM, I don't see how this could ever resolve to a filter declared in a shadow tree, however.

TBH I haven't tested this, myself; it's implemented in FF and Chrome, only, so far, and I don't know whether there is a spec for this with respect to shadow dom. I just played with that feature and loved it when it works and I was hoping that it would still work inside custom components.

@yGuy thanks for spotting that! Could you please file an issue against whatwg/html? It should be specified right there and currently has a rather vague "same document" sentence that is broken. I agree with you on how this should work. If you're willing to write tests per https://github.com/web-platform-tests/wpt for this that might help ensure browsers get it correct sooner. I'd be happy to help guide you in that.

@rniwa maybe there could be some specific syntax allowing shadow DOM items to reach out to search segments outside their shadow tree? Seems to me that reaching out shouldn't be that bad, at least as explicitly specified intention, it's not like reaching in to mess with isolated component.

@rniwa maybe there could be some specific syntax allowing shadow DOM items to reach out to search segments outside their shadow tree? Seems to me that reaching out shouldn't be that bad, at least as explicitly specified intention, it's not like reaching in to mess with isolated component.

Perhaps. The trick is to come up with a proposal that's backwards compatible, which might be tricky.

@rniwa maybe there could be some specific syntax allowing shadow DOM items to reach out to search segments outside their shadow tree? Seems to me that reaching out shouldn't be that bad, at least as explicitly specified intention, it's not like reaching in to mess with isolated component.

Perhaps. The trick is to come up with a proposal that's backwards compatible, which might be tricky.

element.attachShadow({
    mode: 'open',
    delegatesFocus: true,
    xlink: 'parent',
    cssPassThrough: true
})

;) I donno how to write specs ...

dictionary ShadowRootInit {
  required ShadowRootMode mode;
  ShadowRootXLinkMode xlink;
  boolean ShadowRootCSSMode cssPassThrough;
  boolean ShadowRootFocusMode delegatesFocus;
};

enum ShadowRootXLinkMode { "parent", "closed" };

Please file a new issue to discuss that kind of proposal. This issue is used as a meta bug for tracking issues pertaining internal referencing used in SVG.

Was this page helpful?
0 / 5 - 0 ratings