Skip to main content
Ben Nadel at the New York ColdFusion User Group (Sep. 2009) with: Aaron Foss
Ben Nadel at the New York ColdFusion User Group (Sep. 2009) with: Aaron Foss

Practical SVG By Chris Coyier

By
Published in , , Comments (6)

A couple of weeks ago, I tweeted about how daft I felt when it came to SVG or, Scalable Vector Graphics. SVG has been a viable web technology for several years now; and, a general graphics technology for many more years than that. And yet, it was a total blind-spot for me. I didn't know thing-one about it. Luckily, on the very day that I tweeted about my ignorance, Chris Coyier released "Practical SVG" as part of the "Book Apart" collection. I'm not one to look a gift horse in the mouth, so I purchased the book and gave it a read.

This book was exactly the thing that I needed! It shed light on every aspect of SVG from basic usage and browser support all the way through design applications and build tools. It's not an in-depth book on SVG; rather, it's a primer on the technology and its whole landscape - a sort of "review of the literature," if you will. But, it certainly contained enough detail to shatter the mystery and open my eyes up to the possibilities.

The book is a fairly quick read, as most books in the Book Apart series are. I spent a few hours reading it the first time, drinking from the firehose of new information. Then, I went back and skimmed it again, re-reading some of the implementation details, this time with a better mental model.

At this point, I'm certainly no SVG expert; but, as Garrison Keillor might say, this book "gives shy persons the strength to get up and do what needs to be done." So, taking what I learned from "Practical SVG," I wanted to try and implement some SVG-based solutions in an Angular 2 context.

Run this demo in my JavaScript Demos project on GitHub.

To experiment with SVG, I tried to create a "mood rating" component in which the user can select one of five emoticons that most closely represents their current mood. I designed the emoticons in Adobe Fireworks (one of the best graphics programs of all time); and, included each emoticon as an individual, inline SVG document in the component template.

// Import the core angular services.
import { Component } from "@angular/core";
import { EventEmitter } from "@angular/core";

@Component({
	selector: "mood-rating",
	inputs: [ "value", "size" ],
	outputs: [ "valueChange" ],
	styles: [
		`
			:host {
				display: table ;
			}

			.items {
				list-style-type: none ;
				margin: 0px 0px 0px 0px ;
				padding: 0px 0px 0px 0px ;
			}

			.items:after {
				clear: both ;
				content: "" ;
				display: table ;
				height: 0px ;
			}

			.item {
				color: #CCCCCC ;
				cursor: pointer ;
				float: left ;
				margin: 0px 15px 0px 0px ;
				padding: 0px 0px 0px 0px ;
			}

			.item:hover {
				color: #333333 ;
			}

			.item--on,
			.item--on:hover {
				color: inherit ; /* Pull in color from parent, for stroke + fill. */
			}

			.item-icon {
				height: 100% ;
				width: 100% ;
			}
		`
	],
	template:
	`
		<ul class="items">
			<li
				(click)="selectRating( 1 )"
				class="item"
				[class.item--on]="( value === 1 )"
				[style.width.px]="size"
				[style.height.px]="size">

				<svg viewBox="0 0 80 80" preserveAspectRatio="xMidYMid meet" class="item-icon">
					<title>Rating 1</title>
					<path id="Ellipse" d="M 3 40 C 3 19.5652 19.5652 3 40 3 C 60.4348 3 77 19.5652 77 40 C 77 60.4348 60.4348 77 40 77 C 19.5652 77 3 60.4348 3 40 Z" stroke="currentColor" stroke-width="5" fill="none"/>
					<path id="Ellipse2" d="M 16 30.5 C 16 27.4624 18.4624 25 21.5 25 C 24.5376 25 27 27.4624 27 30.5 C 27 33.5376 24.5376 36 21.5 36 C 18.4624 36 16 33.5376 16 30.5 Z" fill="currentColor"/>
					<path id="Ellipse3" d="M 50 30.5 C 50 27.4624 52.4624 25 55.5 25 C 58.5376 25 61 27.4624 61 30.5 C 61 33.5376 58.5376 36 55.5 36 C 52.4624 36 50 33.5376 50 30.5 Z" fill="currentColor"/>
					<path d="M 23.5373 61.4204 C 13.4968 34.4894 66.6081 37.7737 55 61.2328 C 36.966 65.2732 37.1067 58.8905 23.5373 61.4204 Z" fill="currentColor"/>
				</svg>

			</li>
			<li
				(click)="selectRating( 2 )"
				class="item"
				[class.item--on]="( value === 2 )"
				[style.width.px]="size"
				[style.height.px]="size">

				<svg viewBox="0 0 80 80" preserveAspectRatio="xMidYMid meet" class="item-icon">
					<title>Rating 2</title>
					<path id="Ellipse4" d="M 3 40 C 3 19.5652 19.5652 3 40 3 C 60.4348 3 77 19.5652 77 40 C 77 60.4348 60.4348 77 40 77 C 19.5652 77 3 60.4348 3 40 Z" stroke="currentColor" stroke-width="5" fill="none"/>
					<path id="Ellipse5" d="M 16 30.5 C 16 27.4624 18.4624 25 21.5 25 C 24.5376 25 27 27.4624 27 30.5 C 27 33.5376 24.5376 36 21.5 36 C 18.4624 36 16 33.5376 16 30.5 Z" fill="currentColor"/>
					<path id="Ellipse6" d="M 50 30.5 C 50 27.4624 52.4624 25 55.5 25 C 58.5376 25 61 27.4624 61 30.5 C 61 33.5376 58.5376 36 55.5 36 C 52.4624 36 50 33.5376 50 30.5 Z" fill="currentColor"/>
					<path d="M 22.0009 57.5198 C 34.0064 42.2024 46.012 44.6863 57.3965 57.5198 C 43.942 53.38 35.5704 54.9899 22.0009 57.5198 Z" fill="currentColor"/>
				</svg>

			</li>
			<li
				(click)="selectRating( 3 )"
				class="item"
				[class.item--on]="( value === 3 )"
				[style.width.px]="size"
				[style.height.px]="size">

				<svg viewBox="0 0 80 80" preserveAspectRatio="xMidYMid meet" class="item-icon">
					<title>Rating 3</title>
					<path id="Ellipse7" d="M 3 40 C 3 19.5652 19.5652 3 40 3 C 60.4348 3 77 19.5652 77 40 C 77 60.4348 60.4348 77 40 77 C 19.5652 77 3 60.4348 3 40 Z" stroke="currentColor" stroke-width="5" fill="none"/>
					<path id="Ellipse8" d="M 16 30.5 C 16 27.4624 18.4624 25 21.5 25 C 24.5376 25 27 27.4624 27 30.5 C 27 33.5376 24.5376 36 21.5 36 C 18.4624 36 16 33.5376 16 30.5 Z" fill="currentColor"/>
					<path id="Ellipse9" d="M 50 30.5 C 50 27.4624 52.4624 25 55.5 25 C 58.5376 25 61 27.4624 61 30.5 C 61 33.5376 58.5376 36 55.5 36 C 52.4624 36 50 33.5376 50 30.5 Z" fill="currentColor"/>
					<path d="M 16.8629 53.8169 C 36.6421 46.6872 47.632 54.287 59.5915 52 C 54.7617 61.8767 30.4324 51.287 16.8629 53.8169 Z" fill="currentColor"/>
				</svg>

			</li>
			<li
				(click)="selectRating( 4 )"
				class="item"
				[class.item--on]="( value === 4 )"
				[style.width.px]="size"
				[style.height.px]="size">

				<svg viewBox="0 0 80 80" preserveAspectRatio="xMidYMid meet" class="item-icon">
					<title>Rating 4</title>
					<path id="Ellipse10" d="M 3 40 C 3 19.5652 19.5652 3 40 3 C 60.4348 3 77 19.5652 77 40 C 77 60.4348 60.4348 77 40 77 C 19.5652 77 3 60.4348 3 40 Z" stroke="currentColor" stroke-width="5" fill="none"/>
					<path id="Ellipse11" d="M 16 30.5 C 16 27.4624 18.4624 25 21.5 25 C 24.5376 25 27 27.4624 27 30.5 C 27 33.5376 24.5376 36 21.5 36 C 18.4624 36 16 33.5376 16 30.5 Z" fill="currentColor"/>
					<path id="Ellipse12" d="M 50 30.5 C 50 27.4624 52.4624 25 55.5 25 C 58.5376 25 61 27.4624 61 30.5 C 61 33.5376 58.5376 36 55.5 36 C 52.4624 36 50 33.5376 50 30.5 Z" fill="currentColor"/>
					<path d="M 18 47 C 24.1407 53.1407 52.4296 54.5704 60 47 C 50.4149 64.1178 34.805 79.8822 18 47 Z" fill="currentColor"/>
				</svg>

			</li>
			<li
				(click)="selectRating( 5 )"
				class="item"
				[class.item--on]="( value === 5 )"
				[style.width.px]="size"
				[style.height.px]="size">

				<svg viewBox="0 0 80 80" preserveAspectRatio="xMidYMid meet" class="item-icon">
					<title>Rating 5</title>
					<path id="Ellipse13" d="M 3 40 C 3 19.5652 19.5652 3 40 3 C 60.4348 3 77 19.5652 77 40 C 77 60.4348 60.4348 77 40 77 C 19.5652 77 3 60.4348 3 40 Z" stroke="currentColor" stroke-width="5" fill="none"/>
					<path id="Ellipse14" d="M 16 30.5 C 16 27.4624 18.4624 25 21.5 25 C 24.5376 25 27 27.4624 27 30.5 C 27 33.5376 24.5376 36 21.5 36 C 18.4624 36 16 33.5376 16 30.5 Z" fill="currentColor"/>
					<path id="Ellipse15" d="M 50 30.5 C 50 27.4624 52.4624 25 55.5 25 C 58.5376 25 61 27.4624 61 30.5 C 61 33.5376 58.5376 36 55.5 36 C 52.4624 36 50 33.5376 50 30.5 Z" fill="currentColor"/>
					<path d="M 20 48 C 26.1407 54.1407 42.0368 34.4738 59.2953 51 C 49.7102 68.1178 36.805 80.8822 20 48 Z" fill="currentColor"/>
				</svg>

			</li>
		</ul>
	`
})
export class MoodRatingComponent {

	// I hold the dimensions of the emoticons. This value gets applied to both the height
	// and width of each emoticon.
	public size: number ;

	// I hold the current rating.
	public value: number ;

	// I emit a rating selection.
	public valueChange: EventEmitter<number> ;


	// I initialize the component.
	constructor() {

		this.size = 40;
		this.value = 0;
		this.valueChange = new EventEmitter();

	}


	// ---
	// PUBLIC METHODS.
	// ---


	// I emit the selected rating (upholding a one-way data flow).
	public selectRating( newRating: number ) : void {

		( newRating === this.value )
			? this.valueChange.emit( 0 )
			: this.valueChange.emit( newRating )
		;

	}

}

Two powerful features of SVG are that it can scale infinitely and that it can be styled using CSS (Cascading Stylesheets). In this case, I'm exposing a [size] input property that will allow me to render the component at different sizes. I'm also using the "currentColor" as the "fill" and "stroke" properties so that the color of the emoticons can be defined by the calling context (driven by the CSS text color).

In my root component, I'm then rendering the mood-rating component three times, each with a different size and rendering color:

// Import the core angular services.
import { Component } from "@angular/core";

// Import the application components and services.
import { MoodRatingComponent } from "./mood-rating.component";

@Component({
	selector: "my-app",
	directives: [ MoodRatingComponent ],
	template:
	`
		<h3>
			How are you feeling today?
		</h3>

		<p>
			[size]="30"
		</p>

		<mood-rating [(value)]="rating" [size]="30" style="color: #FFC125 ;"></mood-rating>

		<p>
			[size]="75"
		</p>

		<mood-rating [(value)]="rating" [size]="75" style="color: #1C86EE ;"></mood-rating>

		<p>
			[size]="150"
		</p>

		<mood-rating [(value)]="rating" [size]="150" style="color: #FF0099 ;"></mood-rating>
	`
})
export class AppComponent {

	// I hold the current rating.
	// --
	// CAUTION: This value is being used to drive all three mood-rating widget instances.
	public rating: number;


	// I initialize the component.
	constructor() {

		this.rating = 0;

	}

}

And, when we run this in the browser, we get the following output:

Experimenting with SVG in Angular 2.

As you can see, the SVG emoticons look nice a crisp at all three sizes. And, the color styling of the component cascades down into the fill and stroke color of the embedded SVG documents. Super cool stuff! And, the nice thing about an Angular 2 component is that you only pay the price of the payload once. Meaning, I only transfer the SVG content down to the browser as part of the JavaScript payload; then, each instance of the component is nothing but a client-side clone, so to speak, of the original HTML and SVG.

I'm sure that my SVG demo leaves much to be desired in the eyes of SVG experts. But, the point is, a few days ago, I didn't know anything about SVG at all. And now, after having read "Practical SVG" by Chris Coyier, I'm feel empowered. There's still so much to learn; but, I'm headed in the right direction and I have a sense of where I need to go next.

Want to use code from this post? Check out the license.

Reader Comments

1 Comments

Hi Ben, nice article. I'm still a newbie with this SVG world but it looks pretty amazing. I got a question: why do you say that Adobe Fireworks is the best tool for this SVG stuff? I always thought that Illustrator was the best. I don't know at all Fireworks but I want to know your thoughts about it.

15,902 Comments

@Gustavo,

Sorry, I didn't mean to imply that Adobe Fireworks was the best *for SVG*. In fact, it doesn't really have native SVG support - you have to install an Extension just to get "Export SVG" features. I was just saying that *in general* Adobe Fireworks is one of the best graphics programs ever created for web development. It has a wonderful mixture of Bitmap and Vector graphics and is very intuitive to use.

That said, Fireworks has been "end of lifed" by Adobe so it will no longer be updated. I'll probably start to learn more about Sketch App soon ... but I just haven't gotten around to it.

15,902 Comments

Now I'm itching to find more ways to incorporate SVG in my work. I've only scratched the surface - there's so much more you can do with animating elements within the SVG ... and have you all seen these demos for "responsive SVG" floating around on twitter. Kind of crazy stuff. Why are there not enough hours in the day?!

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel