CSS Tricks to add 3D Effects to your Text

Written by Web Developer

December 25, 2023
CSS Tricks to add 3D Effects to your Text

In this article, we will explore a few tricks that allow you to add a touch of 3D to your text content. We won't use complex code and a ton of HTML, only a few lines of CSS and you can have the following result:

Multi-line 3D text

Creating a 3D box

Let’s start with the following HTML code

<span>A Title</span>

and here is the CSS to transform that single line of text into a 3D box:

span { 
  --d: .3em; /* control the depth */
  padding-block: .1em calc(.1em + var(--d));
  padding-inline:.2em calc(.2em + var(--d));
  background:
    conic-gradient(at right var(--d) bottom var(--d),
      #0004 37.5%,#0008 0 75%,#0000 0) 
    #d81a14; /* the main color */
  clip-path: polygon(0 0,calc(100% - var(--d)) 0,100% var(--d),100% 100%,var(--d) 100%, 0 calc(100% - var(--d)));
}

I am using a span to illustrate that the trick works with inline level elements but it can also be used with block level elements such as block and inline-block elements. In the end, we always have one 3D box with our text content.

Now let’s dissect the code. The first thing we are doing is defining the variable --d that will control the 3D effect. You can adjust the value to understand its effect. Then we define the padding.

Without the 3D effect our padding should be:

padding-block: .1em;
padding-inline: .2em;

But we need extra space on the right and bottom side

padding-block: .1em calc(.1em + var(--d)); /* top & bottom */
padding-inline:.2em calc(.2em + var(--d)); /* left & right */

.1em and .2em are arbitrary values so feel free to use whatever you want.

The next step is to add the gradient coloration

Oeverview of the gradient effect

We first apply a solid color as background-color and above it, we use a conic-gradient with three colors. A transparent one and two semi-transparent black to create the different shades of our 3D box.

background:
  conic-gradient(at right var(--d) bottom var(--d),
    #0004 37.5%,#0008 0 75%,#0000 0) 
  #d81a14; /* the main color */

We can use only the conic-gradient and define the final colors there but it requires us to manipulate three colors instead of only one.

The last step is to apply clip-path to cut to corners and get the final shape.

Overview of the clip-path
clip-path: 
  polygon(
   0 0, /* 1 */
   calc(100% - var(--d)) 0, /* 2 */
   100% var(--d), /* 3 */
   100% 100%, /* 4 */
   var(--d) 100%, /* 5 */
   0 calc(100% - var(--d)) /* 6 */
  );

That’s all. With only a few lines of CSS, you get a cool 3D effect. You can easily adjust the code to get a different variation.

I let you dissect the code as a small exercise to understand the changes I have made.

Multi-line 3D effect

Let’s move to the interesting part. The previous effect works only with a single inline sentence or a block-level element but what we want is to support multi-line. We won’t wrap each line in a div or span but we will keep our code simple:

<span>A title <br> with many lines<br> of text</span>

I am using <br> to introduce line breaks but they can also occur naturally when the text doesn’t fit into one line.

If we apply the previous CSS we get the following:

It’s clearly broken but we can fix it with a few adjustments. First of all, clip-path doesn’t play well with inline elements having many lines so let’s remove it.

span { 
  --d: .3em; /* control the depth */
  line-height: 2; /* control the distance between lines */

  padding-block: .1em calc(.1em + var(--d));
  padding-inline:.2em calc(.2em + var(--d));
  background:
    conic-gradient(at right var(--d) bottom var(--d),
      #0004 37.5%,#0008 0 75%,#0000 0) 
    #d81a14; /* the main color */
}

I am introducing the line-height property to control the height of one line or, seen differently, the distance between lines.

A little better but still not there. It’s important to notice the effect of the background and how it splits between the lines, the same for the padding. We can control this effect using box-decoration-break: clone

Each box fragment is rendered independently with the specified border, padding, and margin wrapping each fragment. The border-radius, border-image, and box-shadow are applied to each fragment independently. The background is also drawn independently for each fragment, ref

We are getting closer. We are only missing the cut part that we did with clip-path previously. We are going to use mask for this. It relies on gradients so it will also get “cloned” for each line like the previous decorations:

mask: 
  linear-gradient(45deg,
    #0000   calc(       var(--d)*cos(45deg)),
    #000  0 calc(100% - var(--d)*cos(45deg)),
    #0000 0);

Our effect is now perfect. Here is the full demo with two variations

You can use whatever content you want and as many lines as you want. All you have to do is adjust a few values to control the color, the 3D depth, and the distance between the lines.

Let’s try another kind of 3D effect where the boxes stretch to the end of the line. This time we will work with block-level elements.

The logic of this effect is different and it’s all about repeating gradients following the y-axis and we will use the height of one line for this. Luckily the new lh is what we need here since 1lh is equal to one line of text (more precisely the height of the line box)

Let’s start by defining the parameters:

h2 { 
  --d: .3em; /* control the depth */
  --s: .2em; /* control the space between boxes */
  line-height: 2; /* control the height of the boxes */
}

Then we apply a background configuration similar to the previous effect:

Overview of the repeating gradient
background: 
  conic-gradient(at left var(--d) bottom var(--d),
    #0000 25%,#0008 0 62.5%,#0004 0) 0 0/100% 1lh 
  #2699DC

Note how the height of the gradient is equal to 1lh to make it repeat each line of text.

The next step is to apply a mask to create the space between the lines and at the same time cut the corner to create the 3D effect. Here is a figure to illustrate the gradient configuration of the mask:

Overview of the mask effect

A first gradient will cut the red part and a second one will cut the green part.

mask: 
 conic-gradient(from 90deg at var(--d) var(--s),
     #000 37.5%,#0000 0) 0     0      /51% 1lh repeat-y,
 conic-gradient(from 45deg at calc(100% - var(--d)) calc(100% - var(--s)),
     #0000 62.5%,#000 0) 100% var(--s)/51% 1lh repeat-y

We are almost good, we can already see our 3D effect across multiple lines:

Overview of the 3D effect

But if you look closely, you can notice that the text is not perfectly aligned inside the box. It’s closer to the bottom than the top. We need to offset the whole gradient configuration (background and mask). I will skip the math part and will give you the value of the offset which is (d - s)/2 and the full code will be:

h2 { 
  --d: .3em; /* control the depth */
  --s: .2em; /* control the space between boxes */
  line-height: 2; /* control the height of the boxes */

  padding-inline: calc(var(--d) + .2em) .2em;
  background: 
    conic-gradient(at var(--d) calc(100% - var(--d)),
      #0000 25%,#0008 0 62.5%,#0004 0) 0 calc((var(--d) - var(--s))/2)/100% 1lh 
    #2699dc; /* the main color */
  mask: 
    conic-gradient(from 90deg at var(--d) var(--s),
      #000 37.5%,#0000 0) 0    calc((var(--d) - var(--s))/2)/51% 1lh repeat-y,
    conic-gradient(from 45deg at calc(100% - var(--d)) calc(100% - var(--s)),
      #0000 62.5%,#000 0) 100% calc((var(--d) + var(--s))/2)/51% 1lh repeat-y;
}

The text is now centered within the boxes but the offset created another issue. The box of the last line is cut and we have a strange line at the top.

To fix the first issue we add a padding-bottom and for the second one, we use clip-path to hide the line at the top.

padding-bottom: calc(var(--d)/2);
clip-path: inset(calc(var(--d)/2) 0 0);

Now our effect is perfect. Here is the full demo again

Another 3D effect

let’s try a last one but this time we are going to put letters/numbers inside 3D boxes

As you can see in the code, we are not wrapping each letter inside a container. We are using the same technique as the previous effect but this time following the x-axis. Instead of “each line”, it’s “each letter” and for this, I will rely on the ch unit (instead of the lh). All the letters need to take up the same amount of space so only monospace fonts are suitable for this effect.

I won’t dig into the code because this will be your homework! Try to follow the same steps as I did previously and you will see that I am using almost the same tricks

Discount

Enter Discount Code MOVEME During The SIGN UP Process To Get 90% Off Your First Month

with the discount code

MOVEME

Use Code Now
Jivo Live Chat