title |
before |
folders |
after |
notes |
Set up project |
Before we get started, create some files and get ready.
|
label |
type |
smiley-face |
folder |
|
label |
indent |
index.html |
1 |
|
label |
type |
indent |
css |
folder |
1 |
|
|
|
1. Make an `index.html` & add the boilerplate code.
2. Make a `main.css` in your `css` folder—it can remain empty.
|
label |
text |
Naming conventions |
Don’t forget to follow the [naming conventions](/topics/naming-paths-cheat-sheet/#naming-conventions). |
|
label |
text |
HTML snippets |
Create the boilerplate with `html5`, `viewport`, `css` |
|
|
|
title |
before |
code_lang |
code_file |
code |
lines |
SVG wrapper |
Inside our HTML we’ll start by writing the code for the SVG—specifically defining the dimensions and the artboard.
|
html |
index.html |
⋮
</head>
<body>
<svg class="smiley" width="256" height="256" viewBox="0 0 256 256">
</svg>
</body>
</html>
⋮
|
|
num |
text |
5 |
We can add `class` attributes to the SVG elements.
The `width` & `height` define the dimensions of the image.
---
The `viewBox` defines the artboard in the image, a cropping zone. It’s almost always the same as the width & height.
- `0 0` — the top left corner’s x & y coordinates.
- `256 256` — the width and height of the art board.
|
|
|
|
|
title |
before |
code_lang |
code_file |
code |
lines |
after |
Draw the face circle |
Using SVG’s `<circle>` tag we can create the face.
|
html |
index.html |
⋮
<svg class="smiley" width="256" height="256" viewBox="0 0 256 256">
<circle class="face" cx="128" cy="128" r="120" />
</svg>
⋮
|
|
num |
text |
3 |
- `cx` — the center x coordinate, measured from the left of the image
- `cy` — the center y coordinate, measured from the the top of the image
- `r` — the radius of the circle, measured from the `cx` & `cy`
Notice the closing slash at the end of the `<circle … />` tag: because this is XML self-closing tags **must** include their own slash.
|
|
|
|
You should see a black circle when you refresh in the browser.
*Here’s an explanation of the different attributes on the `<circle>` tag:*
![](face.png)
|
|
title |
before |
code_lang |
code_file |
code |
lines |
after |
Make the face yellow |
SVGs use CSS for much of their visual style—the only difference is the properties that are used to apply visual design.
|
css |
css/main.css |
.face {
fill: gold;
}
|
num |
text |
1 |
We select things the same as with HTML, using classes or tags. |
|
num |
text |
2 |
The `fill` property is used to colour SVG shapes. |
|
|
![](face-yellow.png)
|
|
title |
before |
code_lang |
code_file |
code |
lines |
after |
Add the eyes |
Using two more `<circle>` tags we can add the eyes onto our smiley face.
|
html |
index.html |
⋮
<svg class="smiley" width="256" height="256" viewBox="0 0 256 256">
<circle class="face" cx="128" cy="128" r="120" />
<circle class="left-eye" cx="100" cy="104" r="12" />
<circle class="right-eye" cx="156" cy="104" r="12" />
</svg>
⋮
|
|
num |
text |
4-5 |
Two new circles. We don’t need to add `fill` because they’ll automatically be black. |
|
|
|
![](eyes.png)
|
|
title |
before |
code_lang |
code_file |
code |
lines |
after |
Add the mouth |
Using the SVG `<path>` tag we can create really complex paths and shapes. We can use it now to make the smile.
|
html |
index.html |
⋮
<svg class="smiley" width="256" height="256" viewBox="0 0 256 256">
<circle class="face" cx="128" cy="128" r="120" />
<circle class="left-eye" cx="100" cy="104" r="12" />
<circle class="right-eye" cx="156" cy="104" r="12" />
<path class="mouth" d="M100,160 Q128,190 156,160" />
</svg>
⋮
|
|
num |
text |
6 |
All the fanciness for the `<path>` is controlled by the `d=""` attribute—it gives coordinates for anchor points and for control handles.
- `M100,160` — the starting anchor point for the path.
- `Q128,190` — means we want to use a quadratic bézier curve, giving us only one control handle for the curve. The coordinates are the handle’s location.
- `156,160` — the coordinate for the last anchor point.
|
|
|
|
In the `d=""` attribute there’s a lot of complexity going on, here’s a break down of what each thing is doing.
![](mouth.png)
**You won’t be able to see the mouth in your browser yet.**
|
|
title |
before |
code_lang |
code_file |
code |
lines |
after |
Style the mouth |
We can’t see the mouth in our SVG face yet because the path exists but it doesn’t have a defined, visible stroke. We do that with CSS.
|
css |
css/main.css |
⋮
.face {
fill: gold;
}
.mouth {
fill: none;
stroke: #000;
stroke-width: 6px;
stroke-linecap: round;
}
|
|
num |
text |
8 |
The colour of the stroke on the shape. |
|
num |
text |
9 |
The thickness of the shape’s stroke. |
|
num |
text |
10 |
Makes the ends of the stroke into rounded corners instead of sharp edges. |
|
|
*Now the mouth should be visible in the browser.* |
|
title |
before |
code_lang |
code_file |
code |
lines |
after |
Add the eyebrows |
Let’s use the `<rect>` tag to create a set of eyebrows.
|
html |
index.html |
⋮
<svg class="smiley" width="256" height="256" viewBox="0 0 256 256">
<circle class="face" cx="128" cy="128" r="120" />
<circle class="left-eye" cx="100" cy="104" r="12" />
<circle class="right-eye" cx="156" cy="104" r="12" />
<path class="mouth" d="M100,160 Q128,190 156,160" />
<rect class="left-eyebrow" x="97" y="66" width="6" height="32" rx="4" ry="4" />
<rect class="right-eyebrow" x="153" y="66" width="6" height="32" rx="4" ry="4" />
</svg>
⋮
|
|
num |
text |
7-8 |
There are a bunch of attributes here to create the rectangle:
- `x` — the top left x coordinate
- `y` — the top left y coordinate
- `width` — how wide the rectangle is
- `height` — how tall the rectangle is
- `rx` — the horizontal border-radius size
- `ry` — the vertical border-radius size
|
|
|
|
This is what we should be looking at in our browser right now:
![](eyebrows.png)
|
|
title |
before |
code_lang |
code_file |
code |
lines |
after |
Rotate the eyebrows |
Using the CSS `transform: rotate()` property we can adjust the eyebrows to look happy.
|
css |
css/main.css |
⋮
stroke-width: 6px;
stroke-linecap: round;
}
.left-eyebrow {
transform: rotate(80deg);
}
.right-eyebrow {
transform: rotate(100deg);
}
|
|
**If we look in the browser right now we won’t see the eyebrows any more.** It’s because `transform-origin` is set to the top-left of the SVG by default.
Here’s a little visualization of what’s happening right now:
![](busted-eyebrows.png)
|
|
title |
before |
code_lang |
code_file |
code |
lines |
after |
Fix the eyebrow anchor points |
We want to get the `transform-origin` anchor point in the center of the rectangle, but `center center` doesn’t work on SVG.
So we need to use pixel coordinates instead.
|
css |
css/main.css |
⋮
.left-eyebrow {
transform: rotate(80deg);
transform-origin: 100px 82px;
}
.right-eyebrow {
transform: rotate(100deg);
transform-origin: 156px 82px;
}
|
|
num |
text |
4 |
- The horizontal point, `100px`, is the exact center of the rectangle measured from the left edge.
- The vertical point, `82px`, is the exact center of the rectangle measure from the top edge.
|
|
|
|
With these `transform-origin` properties we can now see the eyebrows exactly where we want them to be.
Here’s a little visualization of where those numbers came from:
![](eyebrow-transform-origin.png)
|
|
title |
before |
code_lang |
code_file |
code |
lines |
after |
Interactivity with transitions |
Let’s add a little interactivity with transitions. We’re going to make the eyebrows rotate into an angry state when you hover over the face.
|
css |
css/main.css |
⋮
.left-eyebrow {
transform: rotate(80deg);
transform-origin: 100px 82px;
transition: all .5s linear;
}
.right-eyebrow {
transform: rotate(100deg);
transform-origin: 156px 82px;
transition: all .5s linear;
}
.smiley:hover .left-eyebrow {
transform: rotate(100deg);
}
.smiley:hover .right-eyebrow {
transform: rotate(80deg);
}
|
|
num |
text |
5 |
Add a `transition` to the default state for the eyebrows so they’ll be animated when hovered. |
|
|
num |
text |
14 |
Read this selector from right-to-left: “choose the `.left-eyebrow` when the user `:hover`s the `.smiley`.
I’ve specifically done this so we don’t have to hover directly on the eyebrow for the animation to trigger, we can hover anywhere on the smiley face.
|
|
|
*Try it out!*
|
|