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>
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);
font-size:var(--f);
/* 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 */
-webkit-mask:
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;
}
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.
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)
Some geometry to understand the 29.29%
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%
clipping
We will use clip-path
to create the folded part at the bottom
clip-path:
polygon(
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 */
);
masking
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:
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 {
left:0;
right:auto;
transform: translate(-29.29%, -100%) rotate(-45deg);
transform-origin: bottom right;
}
I am adding a transparent border and using background-clip to avoid some bad outline that will appear around the shape
Top comments (1)
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: codepen.io/sxdx-the-looper/pen/Vwr...