DOM.uid not compatible with SVG downloading

Example at https://beta.observablehq.com/@fil/contour-labels-svg :slight_smile:

if I use DOM.uid for the mask (to hide the white stroke under the white labels), it will work on Safari, but then I can’t use the downloaded SVG (and, vice-versa.)

Interesting! What follows are some observations from testing and messing around in Firefox, Chrome and Safari (on macOS)… unfortunately, I wasn’t able to figure out how to fix your issue, but maybe some of what follows will be helpful:

Both the downloaded SVG and the SVG embedded in (the current version of) your notebook display properly in both Firefox and Chrome (i.e. the masks properly occlude the strokes).

Using Safari, the masks don’t work when embedded in the notebook but they do appear in the downloaded SVG.

I was able to edit a stroke element of the SVG in your notebook using Safari’s DOM inspector and get the mask to work by doing the following:

  • First, apparently you have to use the -webkit-mask style attribute instead of the mask attribute for Safari. See MDN. I ended up with something like this:

It’s easy enough to edit your notebook so that it adds a -webkit-mask style attribute in addition to the mask attribute which should take care of the first point. I don’t know how to deal with the second one, but I found some weird (browser-dependent) behavior when saving from my fork, which I guess is possible because of the way the serialize function works.

  • If I save the SVG using that notebook in Firefox, the -webkit-mask is replaced by mask in all of the style attributes. Chrome and Safari don’t do this. Fortunately, it seems like the masks work in the resulting SVG in all 3 browsers without issue. (Recall that Safari only had trouble with the notebook version anyways)

  • However, if I open that fork in Safari and then save an SVG, the -webkit-mask property values become url(https://beta.observablehq.com/...#...) instead of just url(#...). Firefox and Chrome don’t do this. Oddly enough, what this means is that the masks in this saved SVG don’t work in Firefox (though they do in Chrome and Safari).

1 Like

The CSS Values specification (albeit draft?) defines the intended behavior for local URLs:

https://www.w3.org/TR/css-values-4/#local-urls

If a url()’s value starts with a U+0023 NUMBER SIGN ( # ) character, parse it as per normal for URLs, but additionally set the local url flag of the url().

When matching a url() with the local url flag set, ignore everything but the URL’s fragment, and resolve that fragment against the current document that relative URLs are resolved against. This reference must always be treated as same-document (rather than cross-document).

Unfortunately, Firefox and Safari don’t (yet?) conform to this behavior, and the base element we add to the sandboxed iframe break local fragments.

I suspect it’d be pretty easy to patch the serialize function in @mbostock/saving-svg to strip the absolute URLs and replace them with fragment identifiers, if you want to fork my notebook and propose some changes. :wink:

2 Likes

I’ve added a little logic to @mbostock/saving-svg so that it now preserves the local-ness of local URLs when serializing.

1 Like

You’re too fast :slight_smile:

1 Like