CSS var() with bubbles

During a recent static build I was tasked with putting decorative "bubbles" throughout the site. I saw this as a chance to see just how far I could push CSS variables and increase the flexibility of the CSS I was producing all while, hopefully, writing a bit less and more structured code.

The bubble

Our bubble is just a div element on the page with CSS applied to it we can create a pretty circle.

<div class="bubble"></div>

A simple div element creates our bubble

.bubble{
    background-image: linear-gradient(#ECA72C, transparent);
    border-radius: 50%;
    height: 100px;
    left: 10px;
    position: absolute;
    top: 10px;
    width: 100px;
}

Our base CSS creates a 100px bubble

Using the ::after pseudo selector we can fill in our bubble in.

.bubble::after{
    background-color: #222;
    border-radius: 50%;
    content: '';
    display: block;
    height: 90px;
    left: 5px;
    position: absolute;
    top: 5px;
    width: 90px;
}

Our ::after pseudo selector fills the bubble in

This isn't a bad option for a single bubble but it isn't really reusable for multiple bubbles, which could be different sizes.It also doesn't include any sort of animation which we may want to tinker with to add a bit of polish.

Adding in var()s

The first step to make our bubble a bit more reusable is to make use of CSS variables to store values we can use or overwrite as needed.

What we're going to do is define some :root variables. For this instance we are just setting up some basic colours we can use along with a demo .bubbles container.

Using CSS variables makes things a little more readable.

:root{
    --background: #222;
    --b-blue: #00A6FB;
    --b-green: #9EE493;
    --b-orange: #EE5622;
    --b-purple: #44355B;
    --b-yellow: #ECA72C;
}

.bubbles{
    background-color: var(--background);
    height: 400px;
    position: relative;
    width: 100%;
}

.bubble{
    background-image: linear-gradient(var(--b-yellow), transparent);
    border-radius: 50%;
    height: 100px;
    left: 10px;
    position: absolute;
    top: 10px;
    width: 100px;
}

.bubble::after{
    background-color: var(--background);
    border-radius: 50%;
    content: '';
    display: block;
    height: 90px;
    left: 5px;
    position: absolute;
    top: 5px;
    width: 90px;
}

Its a nice start for sure. However it doesn't give us multiple bubbles, yet, and the size of our bubble is still fixed as is the colour.

We don't want to fill our :root with variables that will be specific to our bubbles and the good news is we don't have to.

Var()s for our bubbles

I guess you could say this is where the fun begins. We know we want multiple bubbles with different sizes and different colours all in different places.

The first step of this is to define some variables for the bubbles themselves. For now we're pulling out size, colour, position and a few others in to variables which we can overwrite.

Along with using CSS methods such as calc() we can control the size of the centre of our bubble as well giving us some more flexibility to the width of the bubble.

For this demo we only have a limited number of bubbles so our purple bubble here will also get its size defined.

You could separate this out further with bubble sizes as their own classes.

.bubble{
    --b-border-width: 10px;
    --b-colour: var(--b-yellow);
    --b-left: auto;
    --b-position: absolute;
    --b-size: 100px;
    --b-top: auto;
    --b-radius: 50%;
    background-image: linear-gradient(var(--b-colour), transparent);
    border-radius: var(--b-radius);
    height: var(--b-size);
    left: var(--b-left);
    position: var(--b-position);
    top: var(--b-top);
}

.bubble::after{
    background-color: var(--background);
    background-color: var(--background);
    border-radius: var(--b-radius);
    content: '';
    display: block;
    height: calc(var(--b-size) - var(--b-border-width));
    left: calc(var(--b-border-width) / 2);
    position: absolute;
    top: calc(var(--b-border-width) / 2);
    width: calc(var(--b-size) - var(--b-border-width));
}

.bubble--purple{
    --b-border-width: 20px;
    --b-color: var(--b-purple);
    --b-left: 65%;
    --b-size: 300px;
    --b-bottom: 5%;
}

Now we can define multiple bubbles just but adding a div.bubble to our HTML and defining variables for it without having to rewrite out CSS.

All singing, all dancing

We can take this even further. The all singing, all dancing CodePen below shows just how far I have taken (it can be taken further I'm sure!) the use of variables to define multiple bubbles without having to rewrite CSS and define every ::after element gives us some real flexibility.

:root{
    --background: #222;
    --b-purple: #44355B;
    --b-green: #9EE493;
    --b-blue: #00A6FB;
    --b-orange: #EE5622;
    --b-yellow: #ECA72C;
}

@keyframes rotateBubble{
    from{
        transform: rotate(0deg);
    }
    to{
        transform: rotate(359deg);
    }
}

.bubbles{
    background-color: var(--background);
    height: 400px;
    position: relative;
    width: 100%;
}

.bubble{
    --b-animate-time: 10000ms;
    --b-border-width: 10px;
    --b-color: var(--b-yellow);
    --b-left: auto;
    --b-position: absolute;
    --b-size: 100px;
    --b-top: auto;
    --b-radius: 50%;
    --b-right: auto;
    --b-rotate: 0deg;
    --b-bottom: auto;
    animation: rotateBubble var(--b-animate-time) infinite forwards;
    background-image: linear-gradient(var(--b-color), transparent);
    border-radius: var(--b-radius);
    bottom: var(--b-bottom);
    height: var(--b-size);
    left: var(--b-left);
    position: var(--b-position);
    right: var(--b-right);
    top: var(--b-top);
    transform: rotate(var(--b-rotate));
    width: var(--b-size);
}

.bubble::after{
    background-color: var(--background);
    border-radius: var(--b-radius);
    content: '';
    display: block;
    height: calc(var(--b-size) - var(--b-border-width));
    left: calc(var(--b-border-width) / 2);
    position: absolute;
    top: calc(var(--b-border-width) / 2);
    width: calc(var(--b-size) - var(--b-border-width));
}

.bubble--purple{
    --b-animate-time: 5000ms;
    --b-border-width: 20px;
    --b-color: var(--b-purple);
    --b-left: 65%;
    --b-size: 300px;
    --b-rotate: 45deg;
    --b-bottom: 5%;
}

.bubble--green{
    --b-color: var(--b-green);
    --b-left: 20%;
    --b-top: 15%;
}

.bubble--blue{
    --b-color: var(--b-blue);
    --b-left: 40%;
    --b-size: 150px;
    --b-top: 10%;
}

.bubble--orange{
    --b-color: var(--b-orange);
    --b-right: 10%;
    --b-rotate: 90deg;
    --b-top: 2%;
}