DEV Community

Cover image for Fully responsive CSS folded ribbon
Temani Afif for This is Learning

Posted on • Updated on

Fully responsive CSS folded ribbon

In this post, We will create a fully responsive ribbon with no hardcoded width or height. The size and position will dynamically adjust based on the content and we are not going to use a ton of HTML or JS. Only CSS!

Code Structure:

<div class="container" data-ribbon="Ribbon content"></div>
Enter fullscreen mode Exit fullscreen mode

Yes, only one div with the ribbon content as a data attribute.

.container {
  --d:6px;  /* folded part */
  --c:blue; /* color */
  --f:16px; /* ribbon font-size */
  position: relative;
.container::before {
  content: attr(data-ribbon);
  /* I : position & coloration */
  position: absolute;
  top: 0;
  right: 0;
  transform: translate(29.29%, -100%) rotate(45deg);
  transform-origin: bottom left;
  padding: 5px 35px calc(var(--d) + 5px);
  background: linear-gradient(rgba(0,0,0,0.5) 0 0) bottom/100% var(--d) no-repeat var(--c);
  /* II : clipping */
  clip-path: polygon(0 0,100% 0,100% 100%,calc(100% - var(--d)) calc(100% - var(--d)),var(--d) calc(100% - var(--d)) , 0 100%);
  /* III : masking */
      linear-gradient( 135deg, transparent calc(50% - var(--d)*0.707),#fff 0) bottom left,
      linear-gradient(-135deg, transparent calc(50% - var(--d)*0.707),#fff 0) bottom right;
  -webkit-mask-size:300vmax 300vmax;
  -webkit-mask-composite: destination-in;
   mask-composite: intersect;
Enter fullscreen mode Exit fullscreen mode

position & coloration

We color our element using two layers. The main color and a black overlay to the bottom for the folded part. Then we place it in the top right corner.

Ribbon CSS

Now we apply a transformation to have the correct placement and the magic of the responsiveness is here (yes it's the 29.29% value)

Ribbon CSS

Some geometry to understand the 29.29%

Ribbon CSS

From the above we have the following formulas:

cos(45deg) = a/w where w is our element width

x = w - a = w - w*cos(45deg) = w*(1-cos(45deg) = w*(1-0.70710678)

So x = 0,29289322*w ~ 29.29%


We will use clip-path to create the folded part at the bottom

       0 0, /* a */
       100% 0, /* b */
       100% 100%, /* c */
       calc(100% - var(--d)) calc(100% - var(--d)), /* x */
       var(--d) calc(100% - var(--d)), /* y */
       0 100% /* d */
Enter fullscreen mode Exit fullscreen mode

CSS Ribbon


The final touch is to cut the corners using two mask layers. The trick here is to consider a big square with a diagonal gradient inside and we cut at 50% considering the folded part.

An illustration to explain for one corner:

CSS Ribbon

We do the same for the other corner and we consider the intersection of both mask layers using mask-composite: intersect

That's it!

Simply adjust the variables and the content to control the ribbon. We can easily move it to the left side by slightly adjusting the transformation and the position.

.left::before {
  transform: translate(-29.29%, -100%) rotate(-45deg);
  transform-origin: bottom right;
Enter fullscreen mode Exit fullscreen mode

I am adding a transparent border and using background-clip to avoid some bad outline that will appear around the shape

Top comments (1)

sxdx profile image
René Koller

To make this work in Safari (macOS and iOS) the composite mask needs to be changed to

-webkit-mask-composite: destination-in, xor;

See here: