Advanced
On This Page
- Introduction
- Classes
- Comments
- The Use Element
- Nesting SVG Graphics
- Clipping Paths
- Masks
- Describing Your Drawing
- Conclusion
Introduction
We are almost done! By now you've gotten the hang of how to write shapes, paths, curves, and text, how to style them all, and even add gradients. You are pretty much set to start coding your drawings if you haven't already been doing so and getting them prepped for tactile graphics output!
However, there are just a few more techniques and effects to discover that can help make your drawing more efficient and allow for ways to build much more complex shapes and features.
Classes
If you have a lot of shapes in your drawing that are using the same styling properties, but then decide that you need to adjust the stroke width on them all at once, or want to change the color, wouldn't it be great to be able to handle all that with just one parameter change instead of going through every shape and updating them one by one?
Thankfully, SVG offers CSS Classes and id targeting within a <style> area much like the <defs> area. Within the style tags, you can set up your drawing using CSS properties that extend out to specific shapes by pointing to their ids, affect all shapes of a certain type, or any shape after giving it a class.
Classes are created in CSS by starting a name off with a period, like ".myClass" within the style tags. IDs start off with a hashtag or pound/number symbol, like "#myID". Check out this example:
<style>
.blue {
fill: blue;
stroke: darkBlue;
stroke-width: 4;
}
#greenBox {
fill: green;
stroke: gold;
stroke-width: 4;
}
</style>
<rect x="10" y="10" width="50" height="50" class="blue"/>
<ellipse cx="100" cy="30" rx="20" ry="15" class="blue"/>
<rect id="greenBox" x="200" y="10" width="100" height="100"/>
Place the <style> tags at the top of your drawing so it's in an easy to find place, especially as your drawings get more and more complex. Any shape that you add the class property/attribute to will receive all of the styling that appears within the curly braces for the class.
In this case, we have both a class named "blue" and an ID named "greenBox." Note how the Blue class starts off with a period in the style tags and that the greenBox ID starts off with a hashtag.
The circle and the ellipse in the drawing both have class="blue" properties. Note that you do not add the period here, just the name of the class itself. Any shape or path that you add class="blue" to will be styled with all the properties listed within the opening and closing curly braces of the blue class. Changing anything in the blue class will affect all of the shapes using blue as a class.
The square in the drawing has an ID of "greenBox." Note that it uses the hashtag in the style tags, but that you do not use the hashtag when adding the id="greenBox" property to the shape.
IDs
Apart from using id properties to connect specific shapes to styling, another practical use of them is general organization within your drawings.
It's a really good practice to add ids that have meaningful text as a way of identifying exactly what shape you are focusing on, especially in larger drawings where you have many paths or repeating shapes.
Keeping your id values meaningful and understandable also helps others to understand your drawing if you are working with a group or want to pass a drawing on to other people. An id="redBox" is much more understandable than id="rb01", so think carefully about how you want to identify all of your shapes and keep track of them in the future when iterating or returning to a drawing.
Comments
SVG coding uses the exact same syntax as HTML when it comes to adding comments within the code. Start a comment with "<!--" and end a comment with "-->".
Use comments around your drawing to organize it into sections. This makes it much easier to mentally map how your shapes are grouped together, what order they are appearing in the drawing, and helps others if more people will be viewing your code.
<!-- Trunk -->
...
<!-- Branches -->
...
<!-- Canopy -->
...
In this example, comments are being used to identify sections of a tree. The trunk may be made up of one or more shapes and techniques, the branches and canopy as well. Rather than having a ton of shapes, paths, and other items in a big list, breaking sections of your drawing up within commented sections makes it much easier to understand for yourself and others.
Definitely consider this as a best practice, especially for collaborative projects. Comments can span multiple lines and can be used to describe how something is being drawn in the code, to give instructions to other users, or used as bookmark tools to keep track of work in progress or to leave useful notes to yourself as you create.
Anything placed within the <!-- and --> tags will not be rendered and will be ignored outright. If you want to hide parts of a drawing or keep code within your project but not have it render while working out a problem, wrapping it within comment tags accomplishes this.
The Use element
The Use element is extremely useful for keeping your code clean and not repeating yourself. Essentially it allows you to code/replicate shapes that you've already coded without having to copy paste the code everywhere.
A shape can be drawn in the defs section and given an id, then all the Use objects will reference that id to copy the same shape while giving you some basic transform and styling attributes to play with.
Note: While this is being referenced from the defs section, any shape in your code can be replicated with the Use element provided the original shape has an id the Use shape can reference.
<defs>
<circle id="blueCircle" cx="50" cy="50" r="20" fill="blue"/>
</defs>
<use href="#blueCircle" x="100"/>
<use href="blueCircle" x="200" y="100" stroke="orange" stroke-width="5"/>
Note the "href" attribute in the Use elements and how a hashtag is used in front of the id within the quotes. This is the currently recommended way to reference ids within SVG files. Each Use element will draw the exact same circle that is in the defs section, except we have a few attributes we can impart on the copies.
The x and y values give us absolute positioning that overrides the original position of the circle. Any CSS styling attributes not written in the original shape can be added, such as the strokes, but be mindful that any styling added to the original shape will not be able to be changed in the copies.
Nesting SVG Graphics
Throughout this whole tutorial, we've been working by building out shapes all within one <SVG> tag. It is possible to put as many SVG graphics within your primary tag as you'd like, although this can begin to make the code a little more complex.
This is a way for copying and pasting other SVG drawings into your current one, and also a way to keep a drawing contained within it's own canvas space for easier numbers and manipulation, then applying transforms to your nested drawing as a whole.
Think of your primary SVG tag as your blank canvas. That's the final output size of whatever you create. Making a nested SVG tag is like creating a whole new drawing on another piece of paper with different measurements, and then putting that finished drawing on your primary canvas. Utilizing the viewBox, width, and height attributes of the nested SVG tag lets you scale your nested graphics up and down, and applying group transforms to a group tag around the nested SVG lets you move and rotate the graphic around the primary canvas.
Centering a Nested SVG
One common technique is centering a drawing on a sheet of paper. You can always create the drawing within just one SVG tag, but perhaps you've built a design in a 100x100 unit square and want to scale and center it without having to completely redo all the numbers and values for your shapes? The following code snippet depicts a very simple demonstration of this; nesting a small SVG graphic within a primary SVG, doubling the size of the nested graphic, and centering it on the paper.
<svg id="primaryCanvas" version="1.1" width="850" height="1100" xmlns="http://www.w3.org/2000/svg">
<rect id="background" x="0" y="0" width="100%" height="100%" fill="white"/>
<g transform="translate(325, 450)">
<svg id="nestedDrawing" version="1.1" viewBox="0 0 100 100" width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="100" height="100" rx="10" ry="10" stroke="black" stroke-width="4" fill="oldlace"/>
<circle cx="50" cy="50" r="30"stroke="black" stroke-width="4" fill="white"/>
</svg>
</g>
</svg>
Here, we've set up our primary canvas as the outer-most SVG tag. The width and height are set to 850x1100, or the SVG units for an 8.5x11 sheet of letter paper. We've also set up a background rect shape filling the entire width and height of the canvas with white.
We've then opened a group tag with a transform attribute. Remember that the center point of our paper would be (425, 550). With the origin of our nested SVG square in the top-left corner, this means that subtracting half the width and height from the corresponding x and y values of the center will perfectly place the nested graphic right in the middle of the paper.
Following the group tag, we've placed our nested SVG. Note that it also has the SVG tag, but this tag has a viewBox attribute. The viewBox creates a 100x100 unit canvas for the nested SVG, and the width and height values double the size of the initial canvas to 200x200 units.
The nested drawing is a rounded rectangle with a black stroke and an oldLace fill, and there is a white circle with a black border centered on top of the square. Since we only have to worry about units within the 100x100 space, it's easy to set up the attribute values for the shapes.
We close out our nested SVG tag, close the group tag, and finally close our primary SVG canvas tag. All of this works together to perfectly center the little design on the paper and double it's size. Remember to multiply the viewBox width and height values correctly when creating a non-square drawing. Doubling an 80x100 nested graphic would mean that the width and height values for the nested SVG tag would be 160 and 200 respectively.
This process works great if you want to drop a bunch of pre-made SVGs into a collage drawing, want to scale up a drawing you've created without having to redraw everything to the new size, or if you have to position your drawing in a specific place on the paper. The viewBox scaling technique really gives us a large amount of versatility in scaling and adjusting drawings after they've been coded.
Clipping Paths
Clipping paths are another element you can add to the defs section to help create different kinds of shapes in your drawings. Essentially, a clipping path defines an area where shapes are visible.
If a shape uses a clipping path, and if it is positioned outside the area of the clipping path shape, it will not be visible.
If you place a shape halfway into a clipping path area, only half of it will be visible, and so on. It's as if you had a shape or object on a sheet of paper, then took a stencil or another sheet of paper with a shape cut out of it and placed it on top of the shape.
The following example will make a half-circle by creating a clipPath with a rectangle shape that is larger than our circle, and then positioning the circle shape halfway in and out of the clipPath rectangle shape area.
The clipPath shape itself is not visible, only the effects that it has on other shapes, and multiple shapes can use the same clipPath. Pay close attention to the syntax:
<defs>
<clipPath id="clippy">
<rect x="0" y="0" width="100" height="100"/>
</clipPath>
</defs>
<circle cx="100" cy="50" r="20" fill="gold" clip-path="url(#clippy)"/>
Note how we define a clipping path by using the clipPath element in the defs section. The clipPath id is what you use to reference the path with your shapes later on. Any shapes placed within the clipPath tags become areas in which your drawn shapes can be visible.
In this case, it's a 100x100 square. A circle shape is drawn with its center set on the right-most edge of the square. The circle has the clip-path attribute pointing to the id of the clipPath definition.
Pay attention to the syntax as clipPath is how you define the clipping path, and clip-path is the attribute you use to make a shape using the clipPath object.
With the circle positioned as it is and using the clip-path attribute, only the left half of the circle will be drawn/visible on screen or printed in an embosser. The half that lies outside of the clipPath rectangle will not be drawn at all.
Can you think of some interesting shapes you can create by combining basic shapes and paths together using clipping paths?
Think about how a variety of different shapes can be used to form other shapes by cutting away from the main shapes.
If you want to add shading or other detail to a visible shape but not have anything bleed past the edges of the shape, a clipping path really comes in handy!
Note: Don't use CSS styling on your clip path objects. Fill and strokes don't matter and will be ignored. The important part is the actual space the shape or path takes up as the clipPath object.
Using a line shape and giving it a stroke and stroke width to cut a line out of a shape will not work, but defining a path or rectangle that defines the outline of the line you are trying to make will work.
<defs>
<clipPath id="thisDoesNotWork">
<line x1="0" y1="100" x2="300" y2="100" stroke="black" stroke-width="10"/>
</clipPath>
<clipPath id="thisWorks">
<rect x="95" y="0" width="300" height="10"/>
</clipPath>
</defs>
Just to illustrate the point, the first clip path in the example uses a line with a thickness of 10, so if the center of the line starts at (0, 100) the upper part of the line would actually start at (0, 95) and the bottom part of the line would be at (0, 105).
Stroke and Stroke Width aren't seen by clipping paths, so we recreate the line using a rectangle object instead.
Masks
Similar to clipping paths, we can use masks to creatively cut out or reveal single or groups of shapes within our drawings. Instead of using paths, however, masks determine what is visible and what is hidden by using white, black, and greyscale colors which all come together into what is called an alpha matte or mask.
When the mask is applied to a shape, any part of the shape that falls within a white part of the mask will be visible, any part that falls within the black part of the mask will be cut out or invisible, and anything in greyscale will be partially see-through depending on how bright the grey is; the brighter the grey, the more opaque the shape will be.
Masks can be built by importing alpha mattes and greyscale images in from external sources, but for our purposes, we can construct them within the mask object using shapes and paths just like our normal drawings.
The mask objects can exist inside or outside of the defs section, and for organizational purposes it's usually a good idea to keep them near the top of your drawing code and give them understandable and easy to use id names.
To create a mask, we just have to use the <mask> tag and give it an id, draw our alpha matte with a variety of white, black, or greyscale shapes within the mask object, then close the mask tag.
We then apply the mask to a single shape by putting the "mask" attribute into the shape statement just as we do with stroke, fill, etc., and pointing it to the id of the mask, or if you want to apply the mask to a bunch of shapes, just wrap the shapes in a <g> group tag and put the mask attribute on the group. Let's check this out in code:
<mask id="windowCutout">
<rect x="0" y="0" width="100%" height="100%" fill="white"/>
<rect x="100" y="100" width="200" height="400" fill="black"/>
<path d="M 200, 100
V 500
M 100, 300
H 300"
style="stroke: white; stroke-width: 25" fill="none"/>
</mask>
<rect id="background" x="0" y="0" width="100%" height="100%" fill="darkBlue"/>
<rect id="houseWall" x="0" y="0" width="700" height="800" fill="oldLace" mask="url(#windowCutout)"/>
We start off the mask by putting in a white rectangle that fills the entire width and height of the canvas. This ensures that everything we want visible will stay visible.
A black rectangle is drawn on top of the white background to establish the area that will cut out the window shape. A path is put on top of that with white strokes to create the window vertical and horizontal crossbars. After closing the mask tag, there is a background rectangle drawn to fill the whole canvas with a dark blue color. On top of that is the actual house wall rectangle which has the old lace color.
Note how we use the url argument in the mask attribute to apply the mask id to the house wall shape. This will cut out any part of the house wall that is under wherever the mask has a black shape.
Basically every black part of the alpha matte will act like a hole punch. This reveals the dark blue background rectangle behind the house wall and makes a very basic window looking outside.
Greyscale colors/shapes can also be used to make things more translucent instead of fully invisible. You can certainly utilize linear and radial gradients to have shapes gradually fade away as the gradient goes from white to black.
Alternatively, just like with the clipping paths, you can reverse the colors in the mask to only reveal a certain portion of a shape or group of shapes. This can be very useful when you have a series of shapes that you want to have all line up perfectly without having to stress about making all the math, point values, or coordinates perfect.
Just be aware that if you move your shapes around that have masks, you'll need to update your mask layer as well to accommodate for the new position of the shapes.
Describing Your Drawing
Congratulations! You've mastered all the shapes, drawing techniques, and have created a beautiful drawing that's ready to be shared out to the world! We still need to consider digital accessibility with our files apart from the overall goal of print/embossing accessibility, and that means adding a description to your drawing.
When sourcing your SVG drawing online in HTML, you'd apply an alt attribute to the <img> tag and write in your alt text there. SVG files by themselves can have their own embedded alt text and you provide that with the <desc> tag.
<desc>
This is a swanky teal circle with an orange stroke.
</desc>
<circle cx="100" cy="100" r="30" fill="teal" stroke="orange" stroke-width="5"/>
You can write anything you'd like between the <desc> tags, and when opening the SVG file by itself in a browser, the <desc> text will be the alt text for the file.
Follow the same a11y best practices concerning alt text regarding the length of the description, accuracy, detail, etc., but now anyone opening your file will be able to hear and read your description and any other notes you care to include.
Remember, if you build an SVG file and put it up on a website as an inline graphic, meaning you copy and paste the SVG code directly into the website HTML code, make sure you give it an image role by adding role="img" and add alt text with an aria-label attribute, like aria-label="Enter description here."
Describing Individual Shapes or Groups
Additionally, you are able to apply descriptions directly to shapes themselves. This has a whole bunch of interesting applications for making your actual drawings interactive when navigating them with a screen reader.
Just like with Text objects, shapes with descriptions will appear as focusable objects in a browser, and also just like Text, it changes the syntax of the shape code a little bit. Check out the following example of a circle with a description:
<circle cx="100" cy="100" r="25" fill="red">
<desc>I'm a nifty red circle</desc>
</circle>
Note how we no longer close the circle with the "/>" tag, and that it functions more like an HTML element with a full "</circle>" tag.
Essentially, the <desc> tag exists within the shape tags. This will apply to any shape that you add a description to, so a <rect> will have a </rect> after you close your <desc> tag, a <path> will have a </path>, and so on.
For right now, it seems that <desc> tags cannot be applied to groups. A workaround is to create a null or invisible shape within your group and apply the description to that. This gives you the ability to describe something but not have that shape get printed out or rendered.
<g>
<rect x="0" y="0" width="200" height="200" fill="none">
<desc>A cool bunch of boxes.</desc>
</rect>
…
</g>
In the above example, the described rectangle will not actually be rendered visually or in a print, but will be focusable by screen readers when opening the drawing in Chrome or a browser. Anything else within the group will be visible and printable/embossable.
If you intend folks to be able to drag their finger around the screen to find parts of your drawing, note how the width and height of the "null" description rectangle is large enough to accommodate the shapes inside of it, essentially creating the entire touch target area of where you want people to touch and hear the description.
The width and height of the null rectangle/shape doesn't matter if you are not intending on having your drawing being explored on a touch screen. Any values here, including 1, would allow for a screen reader to still find the shape and navigate through all the other descriptions in the drawing no matter the size.
The Title Tag
To continue on with our exploration into SVG accessibility, use a <title> tag to give your SVG file a title that screen readers can read out when focusing on the file in a browser.
Whatever goes between the title tags will get spoken as the actual accessible name of the image, and the description text will get spoken after a screen reader reads out the title text. Having a title prevents screen readers from just saying "image" or "graphic" when focusing on SVG files and images.
Note: Titles and Description text will not show up in the SVG itself as rendered text.
<title>This is a fancy title for my drawing!</title>
It's generally a good practice to put description and title tags up at the top of your file before the definitions section or any other parts of your drawing.
Conclusion
And that's the tutorial! This will remain a lie website that will be updated any time new features of SVG appear that can help us create better and more efficient tactile graphics.
As we all learn and develop new skills, tips, and tricks, I'll surface those on this site as well, especially as we start developing good standards and practices for overall tactile development.
If you find any errors or want to provide feedback on the guide and site overall, please feel free to Contact Me.
</Marco Salsiccia>