SVG is a powerful format, based on XML and is well known for its scalability.
This image format is usually known for its application on logos, icons and graphs, but in this article, we are going to use it with the help of VueJs to create a dynamic styled HTML container.
Prologue
The idea of this project occurred to me while listening to Syntax FM podcast with Sara Soueidan. I never had a chance to use SVG before and I was astonished to hear the countless possibilities that lie within this image format, and I wanted to see it with my own eyes.
The article is going to walk through all the steps necessary to produce a full working component. A full version of the code can be found in this codepen:
vuejs-svg-component-with-filter-and-html-slot-bnejy
The CSS filter property already offers us the possibility to apply a great set of filters to our elements, and even load SVG filter directly with the use of the URL filter function. But in this article we are going to use plain SVGs.
The component
Our first step requires the creation of a Vue component:
<template>
</template>
<script>
export default {};
</script>
Next, we will add a very simple SVG within our Template tags. Our object will include four main parts: basic structure, shape, filter and foreignObject.
Basic structure
First we will create the structure of the SVG. For the purpose of our example we are going to create a simple SVG with a fixed size ViewBox:
<svg
width="600"
height="600"
viewBox="0 0 250 250"
xmlns="http://www.w3.org/2000/svg">
</svg>
Shape
SVGs are well known for the multitude of simple shape elements available within its standards. If you have worked with HTML before, SVG elements will look quite familiar, as they exhibit the same features of a normal HTML element, with the addition of specific XML attributes.
In our example we are going to create a simple Polygon. This shape is described as:
Polygons are made of straight lines, and the shape is "closed" (all the lines connect up).
https://www.w3schools.com/graphics/svg_polygon.asp
The next code will define a polygon made of 4 lines and filled with the colour blue:
<polygon points="5,5 225,15 205,180 10,195" fill="blue"/>
Filter
There are a great number of filter primitives that can be used within an SVG document. Their power comes from the possibility to merge multiple primitives together, to create very complex results, and the ability for the filters to be applied to a single shape/ object within an SVG document. The actual definition of filters is:
The filter SVG element defines a custom filter effect by grouping atomic filter primitives. It is never rendered itself, but must be used by the filter attribute on SVG elements, or the filter CSS property for SVG/HTML elements.
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter
In our example we are going to apply two different filters: feTurbulence and feDisplacementMap. This will provide a distorted effect to all elements in which it is applied.
<filter id="turbulence">
<feTurbulence
type="turbulence"
baseFrequency="0.20"
numOctaves="2"
result="turbulence"></feTurbulence>
<feDisplacementMap
in2="turbulence"
in="SourceGraphic"
scale="25"
xChannelSelector="R"
yChannelSelector="G"></feDisplacementMap>
</filter>
As described above, the filter needs to be used within another object with the use of the filter attribute. In our case, we will have to apply the ID of turbulence to our polygon:
<polygon
points="5,5 225,15 205,180 10,195"
fill="blue"
filter="url(#turbulence)"></polygon>
A rendered version of our component will look like this:
HTML object
Our SVG is finally getting shape. The last step required involves the use of the foreignObject element. This element will allow us to include normal HTML elements within our SVG.
Our first step requires us to add the element within the SVG. This is going to be added alongside the shape, but it is going to be completely independent to it. This means that the filter that we have applied to the polygon will bleed into this SVG element.
<foreignObject
x="5%"
y="10%"
width="90%"
height="90%">
...HTML CODE HERE
</foreignObject>
Now that we have defined our element, we are able to add any HTML we want within its tags. It is important to note that this approach may produce unwanted behaviour across different browsers, and on site performance.
Because of the nature of SVG, all elements within foreignObject element are going to be readable by a screen reader and can be used for accessibility purposes.
For the purpose of our example, we are going to add a few simple elements within our SVG:
<foreignObject
x="5%"
y="10%"
width="90%"
height="90%">
<div style="color:red" xmlns="http://www.w3.org/1999/xhtml">
<p>The HTML works..</p>
</div>
</foreignObject>
TIPS: We are able to use any CSS declaration within the SVG itself and we can even use an existing declaration. For example, we would have defined the
<p>
to be green globally, this declaration would have been applied to our element too.
Our component will render like this:
Make it dynamic
The component above works perfectly, but it is still static. In the following section we are going to make it more dynamic by using features available within the VueJs framework.
Slot
First, we are going to modify the code, so that the content of the foreignObject is actually going to be a slot. These are very useful when you want to provide the flexibility of passing any HTML to the component.
<foreignObject x="5%" y="10%" width="90%" height="90%">
<div style="color:red" xmlns="http://www.w3.org/1999/xhtml">
<slot>
<p>The HTML works..</p>
</slot>
</div>
</foreignObject>
Then we are going to pass some HTML in the parent component that is using our newly introduced slot:
<finalComponent>
<img
src="./assets/logo.png"
width="25px"
height="25px"
alt="Example logo" />
<h1>My heading</h1>
<p>This is my paragraph</p>
</finalComponent>
Running this code in the browser will produce the following design:
Summary
Our fully working VueJs SVG component is now completed. The code can be found at the following codesandbox: vueJs Svg component with HTML slot
Using SVG within a framework, could be a bit tricky, as reactivity and virtual DOM, produce unwanted results, and this example may not be used in production, without the right set of tests.
The article just covers a couple of examples and introduces a few topics, but the possibilities are endless. We could provide a set of shapes as props, introduce SVG animations and finally create a set of filters that can easily be applied to the passed HTML.
I hope this proof of concept turns out to be helpful, and I look forward to seeing different permutations and applications of this approach online.
References
SyntaxFM: https://syntax.fm/show/154/svgs-with-sara-soueidan
SVG definition: https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
CSS filters: https://css-tricks.com/almanac/properties/f/filter/
SVG filters explained: https://tympanus.net/codrops/2019/01/15/svg-filters-101/
SVG filters: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter