Learning to write a custom animated CSS background
A flatiron phase-5 project story
Now at the end of my journey at FlatIron, spanning a fast-paced fifteen weeks, I find myself reflecting on the invaluable lessons learned, especially during the phase 4 and phase 5 periods of the course that culminated in our final solo project. This phase was particularly humbling as I delved into uncharted territory, so to cap this phase off we'll be reading a more "fun" topic. In this blog, I'll share a comprehensive guide to help you implement a cool animated background purely from HTML/CSS to your projects.
Side Note: Your configuration may be different to get it set up in your app, also thank you to Diyorbek Olimov at https://codepen.io, this is where I found the code used in this blog.
First things first, in your index.html you'll want to create a <div> with a class of "wrapper" inside the <body> tag Afterwards make multiple (as many as you want, I stopped at fifteen).
<div><span class="dot"></span></div>
It should look something like this once completed:
<body>
<div class="wrapper">
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
<div><span class="dot"></span></div>
</div>
</body
The "dots" in this instance are for my background which is an underwater theme the dots are bubbles to give an illusion of being underwater.
Next for the CSS, I made a separate CSS file from my main one for this but feel free to throw it in with your main CSS file.
body {
margin: 0;
padding: 0;
overflow: scroll;
height: 100vh;
}
html{
height: 100%
}
body.dark-mode {
background-color: #222;
}
.wrapper.dark-mode {
background: linear-gradient(180deg, #111, 5%, #030423);
}
.wrapper.dark-mode h1 {
color: #fff;
}
.wrapper.dark-mode div {
border-color: rgba(255, 255, 255, 0.7);
}
.wrapper {
height: 100%;
width: 100%;
background: linear-gradient(180deg, #04fafd, 5%, #119dff, 50%, #030423);
position: fixed;
background-size: cover;
z-index: -1;
}
.wrapper h1 {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
position: absolute;
font-family: sans-serif;
letter-spacing: 1px;
word-spacing: 2px;
color: #fff;
font-size: 40px;
font-weight: 888;
text-transform: uppercase;
}
.wrapper div {
height: 60px;
width: 60px;
border: 2px solid rgba(255, 255, 255, 0.7);
border-radius: 50px;
position: absolute;
top: 10%;
left: 10%;
animation: 4s linear infinite;
}
div .dot {
height: 10px;
width: 10px;
border-radius: 50px;
background: rgba(255, 255, 255, 0.5);
position: absolute;
top: 20%;
right: 20%;
}
.wrapper div:nth-child(1) {
top: 20%;
left: 20%;
animation: animate 8s linear infinite;
z-index: -9000;
}
.wrapper div:nth-child(2) {
top: 60%;
left: 80%;
animation: animate 10s linear infinite;
}
.wrapper div:nth-child(3) {
top: 40%;
left: 40%;
animation: animate 3s linear infinite;
z-index: -9000;
}
.wrapper div:nth-child(4) {
top: 66%;
left: 30%;
animation: animate 7s linear infinite;
}
.wrapper div:nth-child(5) {
top: 90%;
left: 10%;
animation: animate 9s linear infinite;
}
.wrapper div:nth-child(6) {
top: 30%;
left: 60%;
animation: animate 5s linear infinite;
}
.wrapper div:nth-child(7) {
top: 70%;
left: 20%;
animation: animate 8s linear infinite;
}
.wrapper div:nth-child(8) {
top: 75%;
left: 60%;
animation: animate 10s linear infinite;
z-index: -9000;
}
.wrapper div:nth-child(9) {
top: 50%;
left: 50%;
animation: animate 6s linear infinite;
z-index: -9000;
}
.wrapper div:nth-child(10) {
top: 45%;
left: 20%;
animation: animate 10s linear infinite;
z-index: -9000;
}
.wrapper div:nth-child(11) {
top: 10%;
left: 90%;
animation: animate 9s linear infinite;
z-index: -9000;
}
.wrapper div:nth-child(12) {
top: 20%;
left: 70%;
animation: animate 7s linear infinite;
z-index: -9000;
}
.wrapper div:nth-child(13) {
top: 20%;
left: 20%;
animation: animate 8s linear infinite;
z-index: -9000;
}
.wrapper div:nth-child(14) {
top: 60%;
left: 5%;
animation: animate 6s linear infinite;
z-index: -9000;
}
.wrapper div:nth-child(15) {
top: 90%;
left: 80%;
animation: animate 9s linear infinite;
z-index: -9000;
}
@keyframes animate {
0% {
transform: scale(0) translateY(0) rotate(70deg);
}
100% {
transform: scale(1.3) translateY(-100px) rotate(360deg);
}
}
These lines of code are only if you implement some sort of toggle for light and dark modes:
body.dark-mode { background-color: #222; } .wrapper.dark-mode { background: linear-gradient(180deg, #111, 5%, #030423); } .wrapper.dark-mode h1 { color: #fff; } .wrapper.dark-mode div { border-color: rgba(255, 255, 255, 0.7); }
So disregard these lines, they aren't needed for this to function.
Nearly done! The next step is getting it to show up in the front end, for this project I used REACT. Import your CSS stylesheet, in my case I named it this:
import "../stylesheets/bubbles.css";
Keep in mind your file structure may be different than mine so how you call your css file may vary.
With all that done you should have a nice animated underwater background, Congratulations! The way I have the CSS for it set up should be seamless with your apps viewport, that was an issue among other tweaks I had to make to make this background act properly.
Reflecting on the Learning Journey:
As I navigated the complexities of implementing this background, I encountered challenges and made adjustments to optimize its behavior in different ways such as the viewport and how it handled light and dark modes. The reward of seeing a beautifully animated underwater background seamlessly integrated into my fully functional full-stack app that I had spent three demanding weeks on was the icing on the cake.
Looking Ahead:
Given my other blogs throughout this journey, I thought a fun one would be nice to cap phase 5 of my course off. As I finish this blog and phase 5 of my course, I'm filled with gratitude for the learning experiences and challenges that I have overcome. The upcoming job search looms on the horizon, promising more adventures to share in future blogs. For now, it's time to unwind and embrace the joy of the approaching holiday season.
In the spirit of continuous growth, stay tuned for insightful blogs chronicling my job search journey. I'm excited to share the next chapter with you!