DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Three Christmas trees in CSS πŸŽ„
Chris Bongers
Chris Bongers

Posted on • Originally published at daily-dev-tips.com

Three Christmas trees in CSS πŸŽ„

I really wanted to make a Christmas tree in CSS, I got to the part where I had the triangle shapes stacked but then I literally got stuck on the shadow...

You see I don't know everything...

I decided to ask Twitter for help, and posted this tweet:

With this, I got some cool ideas from Simon Ramsay, Ꮤ 1 ZΜΆ Ξ” Π― D, and AΚŸα΄‡x Lα΄ΚœΚ€.

So, now I've learned three ways to make Christmas trees in CSS.
Check out this article on how to create them.

Table of contents

  1. CSS Clip-path Christmas tree
  2. CSS Pseudo Christmas tree
  3. CSS Border Christmas tree
  4. Demo

CSS Clip-path Christmas tree

The first method is pretty cool. It uses a clip-path to deduct from a round shape! (This looks much like the border solution, but easier)

For this solution, we use a separate shadow layer, which will push down with a z-index.

The HTML for this solution:

<div class="clip-tree">
  <div class="shadow"></div>
  <div class="layer"></div>
  <div class="shadow"></div>
  <div class="layer"></div>
  <div class="layer"></div>
</div>
Enter fullscreen mode Exit fullscreen mode

Now let's add some CSS to make the magic happen.

We start off by stating the tree itself is a relative element.

.clip-tree {
  position: relative;
  margin-top: 20px;
}
Enter fullscreen mode Exit fullscreen mode

Now we want to start with a green layer:

.clip-tree .layer {
  position: absolute;
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background: green;
  clip-path: polygon(50% 5%, 100% 85%, 100% 100%, 0 100%, 0 85%);
}
Enter fullscreen mode Exit fullscreen mode

As you can see we make a square, and by using the border-radius it will become a circle.

We then add a clip-path which is set the shape we want.

To demonstrate each step check out the image below.

CSS Clip-path flow

Next we want to add the shadow layer:

.clip-tree .shadow {
  position: absolute;
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background: black;
  clip-path: polygon(50% 0%, 0% 110%, 95% 100%);
  margin-top: 20px;
  margin-left: 10px;
}
Enter fullscreen mode Exit fullscreen mode

It is the same setup, but we use a slightly smaller shape and make it black.

Now we need to position each layer:

.clip-tree div:nth-child(1) {
  transform: translateY(5px);
  z-index: 3;
}
.clip-tree div:nth-child(2) {
  transform: translateY(0);
  z-index: 3;
}
.clip-tree div:nth-child(3) {
  transform: translateY(40px);
  z-index: 2;
}
.clip-tree div:nth-child(4) {
  transform: translateY(35px);
  z-index: 2;
}
.clip-tree div:nth-child(5) {
  transform: translateY(70px);
  z-index: 1;
}
Enter fullscreen mode Exit fullscreen mode

As you can see the top layers will have a high z-index and a low translate, the translate will push the layers down a little bit.

Lastly, we add a star on top of our tree

.clip-tree:before {
  content: '⭐️';
  position: absolute;
  left: 42px;
  z-index: 10;
  top: -9px;
}
Enter fullscreen mode Exit fullscreen mode

The end result:

CSS Clip-path Christmas tree

I quite like this method since it's clean, it also seems pretty scalable in terms of making it fully percentage-based.

The difficult part for me is understanding what the clip-path actually does.

Note: There is a super cool clip-path generating tool: Clippy

CSS Pseudo Christmas tree

Another option is to use pseudo-elements, we cut down on some HTML elements, but it can be trickier to scale this version I find.

The HTML structure will look like this:

<div class="pseudo-tree">
  <div class="layer"></div>
  <div class="layer"></div>
  <div class="layer"></div>
</div>
Enter fullscreen mode Exit fullscreen mode

As before the tree itself is a relatively positioned element.

.pseudo-tree {
  position: relative;
}
Enter fullscreen mode Exit fullscreen mode

Each layer will be an absolute position element.

.pseudo-tree .layer {
  position: absolute;
  left: 0px;
  z-index: 2;
}
Enter fullscreen mode Exit fullscreen mode

And then we make the green layer using the :after pseudo-element.

.pseudo-tree .layer:after {
  content: ' ';
  display: block;
  width: 0;
  height: 0;
  border-left: 60px solid transparent;
  border-right: 60px solid transparent;
  border-bottom: 100px solid green;
  border-radius: 50%;
  position: relative;
  z-index: 2;
}
Enter fullscreen mode Exit fullscreen mode

What you see here is that we make use of a border solution to create the shape.

Basically, the shape is a triangle, because of the border-radius, the bottom bit will be curved.

To demonstrate the steps:

CSS Border-radius tree solution

As you can see step 1 is the left border, step 2 is both border left and right, and last is the border-radius.

Now we will need to make the shaded layer using the :before.

.pseudo-tree .layer:before {
  content: ' ';
  display: block;
  border-left: 60px solid transparent;
  border-right: 60px solid transparent;
  border-bottom: 100px solid black;
  border-radius: 100%;
  position: absolute;
  top: 1.1rem;
  left: 0;
  z-index: 1;
  transform: scale(0.74);
}
Enter fullscreen mode Exit fullscreen mode

The same as the after, we are using a black border and positioning it slightly off from the top. We also use transform to scale it down a little bit.

We need to make sure each layer has some space and a downgrading z-index:

.pseudo-tree .layer:nth-child(1) {
  top: 0px;
  z-index: 9;
}
.pseudo-tree .layer:nth-child(2) {
  top: 30px;
  z-index: 8;
}
.pseudo-tree .layer:nth-child(3) {
  top: 60px;
  z-index: 7;
}
Enter fullscreen mode Exit fullscreen mode

Now we want to remove the shadow from the last layer:

.pseudo-tree .layer:last-child:before {
  display: none;
}
Enter fullscreen mode Exit fullscreen mode

And again we can put a star on top:

.pseudo-tree:before {
  content: '⭐️';
  position: absolute;
  left: 52px;
  z-index: 10;
  top: -13px;
}
Enter fullscreen mode Exit fullscreen mode

This will result in the following:

CSS Pseudo-element Christmas Tree

This method for me is the easiest one, it's clean HTML and I just love pseudo-elements.

The one thing that's a bit harder here is to make it fully scalable. It's hard getting this to work off percentages.

Note: There is a cool tool to generate certain shapes in CSS called the CSS Shapes generator.

CSS Border Christmas tree

The last solution is a bit of a mix of the two, it uses pseudo-elements, but in this case to hide stuff we don't need!

To start we will need the following HTML:

<div class="border-tree">
  <div class="layer">
    <div class="cut"></div>
  </div>
  <div class="layer">
    <div class="cut"></div>
  </div>
  <div class="layer">
    <div class="cut"></div>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Now we will start with the CSS for the tree first:

.border-tree {
  position: relative;
  box-sizing: border-box;
}
Enter fullscreen mode Exit fullscreen mode

This is again a relative tree, and because we are using borders for this solution, I've added box-sizing: border-box to now have them expand.

Now, the layer we will use an absolute position and this is where we will define the size of each layer.

.border-tree .layer {
  width: 100px;
  height: 100px;
  position: absolute;
  z-index: 9;
}
Enter fullscreen mode Exit fullscreen mode

Inside the layer, we have the cut element. This is what defines the actual bit.

We did a reverse thing here, where we actually use the border to color the main element!
The background will then be the black shadow.

.border-tree .layer .cut {
  width: 100%;
  height: 100%;
  background: black;
  border-radius: 50%;
  border: 16px solid green;
  box-sizing: border-box;
}
Enter fullscreen mode Exit fullscreen mode

Then we use :before and :after pseudo-elements again to add white rectangles that will cut out the triangle shape!

To see this in steps (the before is red and the after is blue)

CSS Border-radius pseudo Christmas tree

These before and after pseudo elements are build using the following CSS:

.border-tree .layer .cut:before {
  content: '';
  width: 100%;
  height: 100%;
  position: absolute;
  background: #fff;
  opacity: 1;
  z-index: 300;
  transform: rotate(-45deg);
  top: -35%;
  left: 35%;
}
.border-tree .layer .cut:after {
  content: '';
  width: 100%;
  height: 100%;
  position: absolute;
  background: #fff;
  opacity: 1;
  z-index: 300;
  transform: rotate(45deg);
  top: 0%;
  left: -70%;
}
Enter fullscreen mode Exit fullscreen mode

As you can see, they are squares, with a white background, which we rotate and position in place.

Next, we will have to make sure the first one does not have the black background since it's a full green layer:

.border-tree .layer:first-child .cut {
  background: green;
}
Enter fullscreen mode Exit fullscreen mode

The last part of this approach is to position each layer correctly.

.border-tree .layer:nth-child(2) {
  top: 20px;
  z-index: 8;
}
.border-tree .layer:nth-child(3) {
  top: 40px;
  z-index: 7;
}
Enter fullscreen mode Exit fullscreen mode

Also for this one, we will place a star on top:

.border-tree:before {
  content: '⭐️';
  position: absolute;
  left: 42px;
  z-index: 10;
  top: 36px;
}
Enter fullscreen mode Exit fullscreen mode

The result on this tree:

CSS Border pseudo Christmas tree

This method was my first approach, and I think the most sloppy solution. The downside is that you're before and after the element is actually always still there and could overlap other elements on the page.

It's cool to have a play with, but not the most steady solution out there.

Demo πŸŽ„

I've made one codepen to demonstrate these three techniques, which you can find here:

I hope you enjoyed this article and learned something new in how to create certain shapes in CSS.

For me personally, it's still weird a triangle is so difficult to make, especially a "rounded" one.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Top comments (0)

An Animated Guide to Node.js Event Lop

Node.js doesn’t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.