Recently I was working on a very interesting and challenging project. I was told to use CSS wherever possible. That was actually the first time I've heard someone says those words and my heart was full of joy. One of the most challenging features to build was to add a shadow on a crooked border. I know it doesn't sound either glamorous or complicated, but it kinda is.
Crooked border
Here's the desired design for the hero section with crooked border (notice the crooked shadow at the bottom):
Let's break this into smaller modules:
- hero block
- hero title
- hero icon
- crooked border
- inner shadow
First three are easy to create, especially if using flexbox to center the content.
<a class="hero" href="//www.silvestar.codes" target="_blank">
<div class="hero__content">
<h1 class="hero__text">
Star Bist
</h1>
<div class="hero__icon">
<svg>...</svg>
</div>
</div>
</a>
.hero {
position: relative;
overflow: hidden;
}
.hero__content {
display: flex;
flex-direction: column;
align-content: center;
justify-content: center;
}
To create the crooked border, we will use CSS triangle. It's pretty old technique and it's used all over the place. To understand CSS triangles, look at this short animation made by Chris Coyier:
In our case, we'll use :after
pseudo element to create a crooked solid bottom border. Here's the code:
.hero:after {
content: "";
position: absolute;
bottom: 0;
right: 0;
left: 0;
border-bottom: 10em solid white;
border-left: 100vw solid transparent;
}
That wasn't so hard, now let's get to the real problem.
Crooked shadow
To create an inset shadow, we usually use a box-shadow
property. Every box could have a shadow, including our :after
pseudo element. But we cannot apply box-shadow
here because we have a box, although it looks like a triangle and shadow is not crooked.
To solve this problem, I've even tried to rotate another pseudo element with a shadow and place it under an existing triangle. It worked, but not at every viewport size. I wasn't happy with that solution.
Then it hit me—I should use a crooked transparent gradient on a box that is as tall as :after
pseudo element. Wait, what? I'll try to explain this step by step.
First, we should create an element that is as tall as :after
pseudo element. We could use :before
pseudo element for this purpose. Then we should add linear gradient at the correct angle and percentage. In our case, linear gradient should go to the top left corner until 50%. Finally, we should scale and translate element to make it visible.
.hero:before {
content: "";
position: absolute;
bottom: 0;
right: 0;
left: 0;
height: 10em;
background-image: linear-gradient(to top left, #111 45%, transparent 50%);
transform-origin: center center;
transform: scale(1.25) translateY(-.5em);
}
In order to make this work, don't forget to add position: relative
and overflow: hidden
on .hero
element.
And here's the final solution:
Final thoughts
I really enjoyed solving this problem and I'm quite happy with the solution. And it is a fully responsive solution, which is great.
If you like this article or you have a different solution, let me know in the comments or on twitter.
Update
After I posted the article on Reddit, Martijn Hermans provided alternative and perhaps even more elegant solution using skew
and a single pseudo element:
The border and the shadow look sharper now.