The Apple Watch comes with a stock app called Breathe that reminds you to, um, breathe. There’s actually more to it than that, but the thought of needing a reminder to breathe makes me giggle. The point is, the app has this kinda awesome interface with a nice animation.

I thought it would be fun to recreate the design in vanilla CSS. Here’s how far I got, which feels pretty close.
See the Pen Apple Watch Breathe App Animation by Geoff Graham (@geoffgraham) on CodePen.
Making the circles
First things first, we need a set of circles that make up that flower looking design. The app itself adds a circle to the layout for each minute that is added to the timer, but we’re going to stick with a static set of six for this demo. It feels like we could get tricky by using ::before
and ::after
to reduce the HTML markup, but we can keep it simple.
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
We’re going to make the full size of each circle 125px
which is an arbitrary number. The important thing is that the default state of the circles should be all of them stacked on top of one another. We can use absolute positioning to do that.
.circle {
border-radius: 50%;
height: 125px;
position: absolute;
transform: translate(0, 0);
width: 125px;
}
Note that we’re using the translate
function of the transform
property to center everything. I had originally tried using basic top, right, bottom, left
properties but found later that animating translate
is much smoother. I also originally thought that positioning the circles in the full expanded state would be the best place to start, but also found that the animations were cumbersome to create that way because it required resetting each one to center. Lessons learned!
If we were to stop here, there would be nothing on the screen and that’s because we have not set a background
color. We’ll get to the nice fancy colors used in the app in a bit, but it might be helpful to add a white background for now with a hint of opacity to help see what’s happening as we work.
See the Pen Apple Watch Breathe App – Step 1 by Geoff Graham (@geoffgraham) on CodePen.
We need a container!
You may have noticed that our circles are nicely stacked, but nowhere near the actual center of the viewport. We’re going to need to wrap these bad boys in a parent element that we can use to position the entire bunch. Plus, that container will serve as the element that pulses and rotates the entire set later. That was another lesson I had to learn the hard way because I stubbornly did not want the extra markup of a container and thought I could work around it.
We’re calling the container .watch-face
here and setting it to the same width and height as a single circle.
<div class="watch-face">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
Now, we can add a little flex
to the body element to center everything up.
body {
background: #000;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
See the Pen Apple Watch Breathe App – Step 2 by Geoff Graham (@geoffgraham) on CodePen.
Next up, animate the circles
At this point, I was eager to see the circles positioned in that neat floral, overlapping arrangement. I knew that it would be difficult to animate the exact position of each circle without seeing them positioned first, so I overrode the transform
property in each circle to see where they’d land.
We could set up a class for each circle, but using :nth-child
seems easier.
.circle:nth-child(1) {
transform: translate(-35px, -50px);
}
/* Skipping 2-5 for brevity... */
.circle:nth-child(6) {
transform: translate(35px, 50px);
}
It took me a few swings and misses to find coordinates that worked. It ultimately depends on the size of the circles and it may take some finessing.
See the Pen Apple Watch Breathe App – Step 3 by Geoff Graham (@geoffgraham) on CodePen.
Armed with the coordinates, we can register the animations. I removed the transform
coordinates that were applied to each :nth-child
and moved them into keyframes:
@keyframes circle-1 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(-35px, -50px);
}
}
/* And so on... */
I have to admit that the way I went about it feels super clunky because each circle has it’s own animation. It would be slicker to have one animation that can rule them all to push and re-center the circles, but maybe someone else reading has an idea and can share it in the comments.
Now we can apply those animations to each :nth-child
in place of transform
:
.circle:nth-child(1) {
animation: circle-1 4s ease alternate infinite;
}
/* And so on... */
Note that we set the animation-timing-function
to ease
because that feels smooth…at least to me! We also set the animation-direction
to alternate
so it plays back and forth and set the animation-iteration-count
to inifinite
so it stays running.
See the Pen Apple Watch Breathe App – Step 4 by Geoff Graham (@geoffgraham) on CodePen.
Color, color, color!
Oh yeah, let’s paint this in! From what I can tell, there are really only two colors in the design and the opacity is what makes it feel like more of a spectrum.
The circles on the left are a greenish color and the ones on the right are sorta blue. We can select the odd-numbered circles to apply the green and the even-numbered ones to apply the blue.
.circle:nth-child(odd) {
background: #61bea2;
}
.circle:nth-child(even) {
background: #529ca0;
}
Oh, and don’t forget to remove the white background from the .circle
element. It won’t hurt anything, but it’s nice to clean up after ourselves. I admittedly forgot to do this on the first go.
See the Pen Apple Watch Breathe App – Step 5 by Geoff Graham (@geoffgraham) on CodePen.
It’s also at this point that others in the comments have suggested that replacing opacity
for mix-blend-mode
with a value of screen
makes for a nicer way to blend the colors of the circles. I’ve since updated the demos and the code.
Pulse and rotate
Remember that pesky .watch-face
container we created? Well, we can animate it to pulse the circles in and out while rotating the entire bunch.
I had totally forgotten that transform
functions can be chained together. That makes things a little cleaner because it allows us to apply scale()
and rotate()
on the same line.
@keyframes pulse {
0% {
transform: scale(.15) rotate(180deg);
}
100% {
transform: scale(1);
}
}
…and apply that to the .watch-face
element.
.watch-face {
height: 125px;
width: 125px;
animation: pulse 4s cubic-bezier(0.5, 0, 0.5, 1) alternate infinite;
}
Like the circles, we want the animation to run both ways and repeat infinitely. In this case, the scale drops to a super small size as the circles stack on top of each other and the whole thing rotates halfway on the way out before returning back on the way in.
I’ll admit that I am not a buff when it comes to finding the right animation-timing-function
for the smoothest or exact animations. I played with cubic-bezier
and found something I think feels pretty good, but it’s possible that a stock value like ease-in
would work just as well.
All together now!
Here’s everything smushed into the same demo.
See the Pen Apple Watch Breathe App Animation by Geoff Graham (@geoffgraham) on CodePen.
Now, just remember to breathe and let’s all have a peaceful day. ☮️
I’d say this calls for a
mix-blend-mode
on those circles, for all but IE/Edge, likemix-blend-mode: screen
.Great idea!
Great explanation of how you made this. I like the result.
I was trying to get the colors to blend a bit better and used
mix-blend-mode: color-dodge;
on the circles. It’s not perfect, but it’s a bit closer to Apple’s.Hah, I see C. M. Helmer had the same idea!
For sure, it’s a great idea — I may have to play with that and tweak this up depending on the results. :)
Great walk through, and kudos for the clean, simple css!
Awesome demo! Thank you!
I like such simple tutorials, please post more like this. Nice job!
Try adding
mix-blend-mode: screen;
to the.circle
rule.Yup, that’s a great idea and been suggested a couple of times above.
Haha, great job! I also liked the simplicity of the tutorial. Thanks for sharing.
Great job! You can also try without adding other animation for each circle but use transform-origin, like here https://codepen.io/anon/pen/Xzqazq. It’s not quite the same, but similar.
Ah nice, that’s a much more elegant solution! Thanks for the tip. :)
That’s a great addition and a more elegant way to go about this. Thanks for the tip!
Well, not related to the code example, but rather the app itself – as amusing as it may sound, it’s actually very useful for people with various types of neurosis or anxiety, like me for example (very mild case though, but it relaxes me greatly).
So, yeah, it’s actually pretty nifty :D
Great example btw!
Cool tutorial Geoff, It inspired me to try and make a square one for fun! https://codepen.io/o-sewell/full/GOXLLz/
thank you!
I love it! Nice work. :)
Great result! Made something similar in Swift for iOS BreatheView.