NOTE: Be advised that this plugin is in alpha status and not yet extensively tested.
Inline SVG in an HTML5 document has some important advantages:
-
You save on
http
requests which can matter quickly if you're using a lot of SVG icons, for instance. -
You can style your SVG with CSS.
And since you can reference SVG elements across your HTML5 document with
SVG's use
element, you can display them multiple times without creating
additional bandwidth.
A good way to do this is to put an SVG block at the beginning of your
document that includes the graphical elements you want to use in symbol
elements. For instance:
<!-- Begin SVG definition block -->
<svg>
<defs>
<!-- Define e.g. gradients here -->
</defs>
<symbol id="one">
<!-- Your SVG here -->
</symbol>
<symbol id="two">
<!-- Your second SVG here -->
</symbol>
<!-- etc. -->
</svg>
<!-- End SVG definition block -->
<!-- Then use them in the document. -->
<svg viewBox="0 0 300 150">
<use xlink:href="one"/>
</svg>
While this is absolutely wonderful and powerful from a technical point of view, doing this manually can become somewhat tiresome:
-
You probably want to create your SVGs separately in a vector graphics program like Inkscape or Illustrator. That means you have to create the definition block from the individual files. This can be done automatically with a task manager or even a shell script, but:
-
There is some quirkyness about the placement of the
viewBox
attribute; if you want to scale your SVG graphics in CSS by only defining eitherwidth
orheight
, with the other dimension scaling accordingly, you need to define theviewBox
on the referencingsvg
element as in the example above. That means you have to keep track of the appropriate viewbox dimensions for your SVGs. -
If you use
defs
, like for instance, gradients to be referenced in thefill
attribute, you have to move thisdefs
element out of your original SVG to the top of the definition block, as in the example above.
This is where EasySVG come in. It provides methods to be used in Twig that do the work behind the scenes. You just define what SVG files you want to use, and then reference your SVGs by an ID of your choice.
In addition, EasySVG provides a few selected methods for deleting SVG elements and attributes that might make some editing tasks easier (see below).
The Svg Symbols Plugin is for Grav CMS.
FIXME: Description of the benefits of inlining SVG and
referencing it with use
. Problems, EasySVG solves.
FIXME: Describe handling of xlink:href
.
To install this plugin, just download the zip version of this repository and unzip it under /your/site/grav/user/plugins
. Then, rename the folder to svg-symbols
. You can find these files on GitHub or via GetGrav.org.
You should now have all the plugin files under
/your/site/grav/user/plugins/svg-symbols
NOTE: This plugin is a modular component for Grav which requires Grav and the Error and Problems to operate.
Before configuring this plugin, you should copy the user/plugins/svg-symbols/svg-symbols.yaml
to user/config/plugins/svg-symbols.yaml
and only edit that copy.
Here is the default configuration and an explanation of available options:
enabled: true
container_attributes: 'width="0" height="0" style="position:absolute;"'
remove:
- dimensions: true
- script_elements: true
domains_allowed:
- example.com
prefix: 'symbol'
write_with_php_dom: false
Note that if you use the admin plugin, a file with your
configuration, and named svg-symbols.yaml will be saved in
the user/config/plugins/
folder once the configuration is
saved in the admin.
{% do svg.add('theme://images/smiley.svg', 'mysmiley') %}
{{ svg.symbols() }}
{{ svg.use('mysmiley') }}
Say, you have a cute smiley SVG:
smiley.svg:
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="150" height="150" viewBox="0 0 15 15">
<defs>
<radialGradient id="rg"
cx=".7" cy=".3" r=".5"
fx=".5" fy=".5">
<stop offset="20%" stop-color="yellow"/>
<stop offset="100%" stop-color="#Fa0"/>
</radialGradient>
</defs>
<circle id="cic" cx="7.5" cy="7.5" r="7" stroke-width="1"
fill="url(#rg)" stroke="black" />
<circle cx="4.5" cy="5.5" r="1" fill="black"/>
<circle cx="10.5" cy="5.5" r="1" fill="black"/>
<path stroke-width="1" stroke="black" fill="none"
d="M4 8.5 C5 12, 10 12, 11 8.5"/>
</svg>
Then, the {{ svg.use() }}
clause in Twig will insert
(without indentation and comments):
<svg width="0" height="0" style="position:absolute;">
<defs>
<radialGradient id="rg"
cx=".7" cy=".3" r=".5"
fx=".5" fy=".5">
<stop offset="20%" stop-color="yellow"/>
<stop offset="100%" stop-color="#Fa0"/>
</radialGradient>
</defs>
<symbol id="symbol-mysmiley">
<circle id="cic" cx="7.5" cy="7.5" r="7" stroke-width="1"
fill="url(#rg)" stroke="black" />
<circle cx="4.5" cy="5.5" r="1" fill="black"/>
<circle cx="10.5" cy="5.5" r="1" fill="black"/>
<path stroke-width="1" stroke="black" fill="none"
d="M4 8.5 C5 12, 10 12, 11 8.5"/>
</symbol>
<!-- If you have added other SVG files with svg.add() they would come here.
-->
</svg>
and {{ svg.use('mysmiley') }}
will render (again without indentation) as:
<svg viewBox="0 0 15 15">
<use xlink:href="#symbol-mysmiley"/>
</svg>
You can invoke svg.use()
as often as you want in a
document, it will always insert only the latter markup
containing <use>
That's basically the whole point about
this practise.
The following is particularily noteworthy:
-
The
<defs>
element has been moved out of the<symbol>
element defining the original SVG file and is the first element of the containing SVG. This is necessary in order for e.g. gradients as fill patterns to work. -
The
viewBox
attribute is defined on the referencing<svg>
clause (the one containing<use>
, not on the<symbol>
.
Each id
attribute used in the markup is prefixed with
symbol-
. This is to minimize the risk of name
clashes. Normally, you don't have to deal with this. But
should you ever want to reference, for instance in CSS,
those IDs outside of EasySVG, you have to be aware of
it. (Generally you shouldn't, but that's up to you ...)
{% do svg.removeAttribute('smiley', '//circle[1]', 'stroke') %}
This removes the stroke
attribute from the first circle
element in the symbol. The first argument is, as before, the
id, the second is an XPath expression that should return
one or more elements from which the attribute given in the
third argument is to be removed.
The primary intended purpose of this is to remove attributes
like fill
, stroke
, stroke-width
etc. that you want to
style in CSS. This way, your SVG can look good while you're
editing it in Inkscape, without hindering your ability to
apply CSS.
If the XPath expression returns an attribute node_, you can omitt the third argument. The above is equivalent to:
{% do svg.removeAttribute('smiley', '//circle[1]/@stroke') %}
! Naturally, you can only manipulate the SVG before you
! insert it into the document with svg.symbols()
. Since
! EasySVG does its reformatting only then, not on svg.add()
,
! your XPath expression has to match against the original
! SVG's document structure. For instance, the document root is
! matched by /svg
not by /symbol
.
{% do svg.setAttribute('mysmiley', '//circle[1]', 'fill', 'red') %}
{% do svg.setAttribute('mysmiley', '//circle[1]', 'fill', false) %}
- Fix a couple of minor issues.
- Write documentation.
- Build a demo site.
It would be really cool to get live injection of SVG, cloning the logic that live.js applies to CSS. This way, you could edit your SVG in Inkscape or Illustrator and see the results live in your browser.