<!DOCTYPE html>
<html>
<head>
<!-- Uncomment for use the Chrome import feature, instead of inlining the gradient defintions. Note you will need a polyfill for safari. The import will fail for FF, which is fine, it doesn't need the inline gradient workaround.
<script>
function handleLoad(e) {
console.log('Loaded import: ' + e.target.href);
}
function handleError(e) {
console.log('Error loading import: ' + e.target.href);
}
</script>
<link rel="import" href="gradients.svg" onload="handleLoad(event)" onerror="handleError(event)">
-->
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
<style>
.objShape {
width:50px;
height:50px;
}
.objShape1 {
width:100px;
height:100px;
}
.objShape2 {
width:150px;
height:150px;
}
.objStyle1 { --c-stroke: red; --c-stroke-width: 1; --c-fill: url(#grad1); --r-fill: url(#grad2); --r-stroke: blue; --r-stroke-width: 4; }
.objStyle2 { --c-stroke: blue; --c-stroke-width: 2; --c-fill: url(#grad2); --r-fill: url(#grad3); --r-stroke: green; --r-stroke-width: 3; }
.objStyle3 { --c-stroke: green; --c-stroke-width: 3; --c-fill: url(#grad3); --r-fill: url(#grad4); --r-stroke: black; --r-stroke-width: 2; }
.objStyle4 { --c-stroke: yellow; --c-stroke-width: 4; --c-fill: url(#grad4); --r-fill: url(#grad1); --r-stroke: red; --r-stroke-width: 1; }
code {color: blue}
</style>
</head>
<body>
<h2>Instantiating multiple instances of an external svg object, with css styling and gradients </h2>
<h3>The "fill: url(file#gradient)" problem</h3>
<p>
SVG <use> to external svg file with gradients doesn't work for chrome or safari.
Only Firefox works. Chrome and Safari will display the non gradient parts of the svg okay, but will insert a default plain fill for all the gradients.
The issue has to do with a bug in Chrome and Safari which will only process "fill: url(file#gradient)" properly if 'file' is the local file, i.e. 'file' is an empty string.
One well known solution is to inline the entire SVG, but that defeats the benefit of putting the svg external.
</p>
<p>
It turns out that for chrome and safari its sufficient just to inline the gradient definitions into the html file, and leave everything else in the external svg file. Not perfect, but its still less inline svg than the alternative.
In order not to break FF with this workaround, you must also leave the gradient definitions in the external svg file.
Server side processing will allow you to maintain a single svg gradient definition file, if you wish, and then insert it into both the parent html file and the svg file containing the rest of the graphic.
Alternatively you can use <code> <link rel="import" href="gradients.svg"></code> to insert the gradient definitions on the client side. Currently only chrome supports this natively but there is a polyfil for other browsers.
I have tested the link/import on chrome but not the polyfil on safari.
In this demo, the gradient definitions have been statically inlined. You can find the code for the link/import in the comments.
</p>
<h3>Using CSS variables to pass styling</h3>
<p>
You can pass specific css properties in the <use> invocation but those will have global effect upon the entire svg graphic. e.g. <code><use fill="red" xlink:href="test.svg#my-object"/> </code>
</p>
<p>
To target unique styling to an individual svg object, you can use css variables. They can be defined in single class, and that class included in the <use> invocation, e.g.: <code><use class="style1" xlink:href="test.svg#my-object"/></code>.
CSS variables have a mechanism to allow default styles to be specified, thus you don't have to exhaustively specify all variables at invocation if you don't want to. I haven't included fallbacks in this demo.
</p>
<h3>Putting it together</h3>
<ul>
<li> The 12 graphics below are unique clones of "my-object" which is declared externally in "test.svg".
<li> There are 4 unique color classes and 3 unique size classes, which are defined in this html file.
<li> The gradient definitions are inlined at the bottom of this html file (and used by chrome and safari). They are also inlined in test.svg and used by FF.
<li> I've tested on Chrome and Firefox for desktop and Chrome and Safari on ios 10.2.1 using an iphone 6.
</ul>
<svg class="objShape"> <use class="objStyle1" xlink:href="test.svg#my-object"/> </svg>
<svg class="objShape"> <use class="objStyle2" xlink:href="test.svg#my-object"/> </svg>
<svg class="objShape"> <use class="objStyle3" xlink:href="test.svg#my-object"/> </svg>
<svg class="objShape"> <use class="objStyle4" xlink:href="test.svg#my-object"/> </svg>
<br>
<svg class="objShape1"> <use class="objStyle1" xlink:href="test.svg#my-object"/> </svg>
<svg class="objShape1"> <use class="objStyle2" xlink:href="test.svg#my-object"/> </svg>
<svg class="objShape1"> <use class="objStyle3" xlink:href="test.svg#my-object"/> </svg>
<svg class="objShape1"> <use class="objStyle4" xlink:href="test.svg#my-object"/> </svg>
<br>
<svg class="objShape2"> <use class="objStyle1" xlink:href="test.svg#my-object"/> </svg>
<svg class="objShape2"> <use class="objStyle2" xlink:href="test.svg#my-object"/> </svg>
<svg class="objShape2"> <use class="objStyle3" xlink:href="test.svg#my-object"/> </svg>
<svg class="objShape2"> <use class="objStyle4" xlink:href="test.svg#my-object"/> </svg>
<!-- JS needed for the <link rel="import" ...>
<script>
var link = document.querySelector('link[rel="import"]');
var content = link.import;
// Grab DOM from test.svg's document.
var el = content.querySelector('svg');
var child = el.cloneNode(true);
document.body.appendChild(child);
</script>
-->
<!-- statically inlined gradient definitions -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="visibility:hidden; width:0; height:0;">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:blue; stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(230,230,255); stop-opacity:1" />
</linearGradient>
<linearGradient id="grad2" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(230,255,230); stop-opacity:1" />
<stop offset="100%" style="stop-color:green; stop-opacity:1" />
</linearGradient>
<linearGradient id="grad3" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:black; stop-opacity:1" />
<stop offset="100%" style="stop-color:white; stop-opacity:1" />
</linearGradient>
<linearGradient id="grad4" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(255,230,230); stop-opacity:1" />
<stop offset="100%" style="stop-color:red; stop-opacity:1" />
</linearGradient>
</defs>
</svg>
</body>
</html>
// Code goes here
/* Styles go here */
#Instantiating multiple instances of an external svg object, with css styling and gradients
##The "fill: url(file#gradient)" problem
SVG `<use>` to external svg file with gradients doesn't work for chrome or safari. Only Firefox works. Chrome and Safari will display the non gradient parts of the svg okay, but will insert a default plain fill for all the gradients. The issue has to do with a bug in Chrome and Safari which will only process `fill: url(file#gradient)` properly if 'file' is the local file, i.e. 'file' is an empty string. One well known solution is to inline the entire SVG, but that defeats the benefit of putting the svg external.
It turns out that for chrome and safari its sufficient just to inline the gradient definitions into the html file, and leave everything else in the external svg file. Not perfect, but its still less inline svg than the alternative. In order not to break FF with this workaround, you must also leave the gradient definitions in the external svg file. Server side processing will allow you to maintain a single svg gradient definition file, if you wish, and then insert it into both the parent html file and the svg file containing the rest of the graphic. Alternatively you can use `<link rel="import" href="gradients.svg">` to insert the gradient definitions on the client side. Currently only chrome supports this natively but there is a polyfil for other browsers. I have tested the link/import on chrome but not the polyfil on safari. In this demo, the gradient definitions have been statically inlined. You can find the code for the link/import in the comments.
##Using CSS variables to pass styling
You can pass specific css properties in the `<use>` invocation but those will have global effect upon the entire svg graphic. e.g. `<use fill="red" xlink:href="test.svg#my-object"/>`
To target unique styling to an individual svg object, you can use css variables. They can be defined in a single class, and that class included in the `<use>` invocation, e.g.: `<use class="style1" xlink:href="test.svg#my-object"/>`. CSS variables have a mechanism to allow default styles to be specified, thus you don't have to exhaustively specify all variables at invocation if you don't want to. I haven't included fallbacks in this demo.
##Putting it together
- The 12 graphics are unique clones of "my-object" which is declared externally in "test.svg".
- There are 4 unique color classes and 3 unique size classes, which are defined in the html file.
- The gradient definitions are inlined at the bottom of the html file (and used by chrome and safari). They are also inlined in test.svg and used by FF.
- I've tested on Chrome and Firefox for desktop and Chrome and Safari on ios 10.2.1 using an iphone 6.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:blue; stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(230,230,255); stop-opacity:1" />
</linearGradient>
<linearGradient id="grad2" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(230,255,230); stop-opacity:1" />
<stop offset="100%" style="stop-color:green; stop-opacity:1" />
</linearGradient>
<linearGradient id="grad3" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:black; stop-opacity:1" />
<stop offset="100%" style="stop-color:white; stop-opacity:1" />
</linearGradient>
<linearGradient id="grad4" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(255,230,230); stop-opacity:1" />
<stop offset="100%" style="stop-color:red; stop-opacity:1" />
</linearGradient>
<symbol id='my-object' viewBox="0 0 100 100">
<rect width="100" height="100" style="fill: var(--r-fill); stroke: var(--r-stroke); stroke-width: var(--r-stroke-width)" />
<circle cx="50" cy="50" r="45" style="stroke: var(--c-stroke); stroke-width: var(--c-stroke-width); fill: var(--c-fill)" />
</symbol>
</defs>
</svg>