hello people! in this post, i will show you how to make a button with the theme Cyberpunk 2077 using just CSS. let's go samurai, we a have a text editor to burn 🔥
Get Colors, Fonts and HTML structure
Colors
to this post, i will use some specific color pallete, i will list them here:
- yellow (primary):
#fcee09
- black:
#050a0e
- blue:
#00f0ff
- red (secondary):
#ff003c
i published this color scheme on adobe colors too, so you can download it as CSS, SASS or SCSS.
Fonts
for fonts, let's import some from google fonts, i picked the following set of fonts: Barlow and Tommorrow.
@import url("https://fonts.googleapis.com/css?family=Barlow|Tomorrow:400,700&display=swap");
HTML
for now, we will just use a pure <button>
tag with some classes.
<button class="btn">Get your copy now_</button>
Building the Primary button
now let's start with the primary state of the button
@import url("https://fonts.googleapis.com/css?family=Barlow|Tomorrow:400,700&display=swap");
:root {
--yellow-color: #fcee09;
--red-color: #ff003c;
--black-color: #050a0e;
--blue-color: #00f0ff;
}
.btn {
display: flex;
align-items: center;
justify-content: center;
border: 0;
outline: none;
background-color: var(--yellow-color);
color: var(--black-color);
cursor: pointer;
padding: 20px 25px;
position: relative;
font-family: Tomorrow, sans-serif;
font-size: .85rem;
text-transform: uppercase;
}
at this point you should have something like this:
Adding the folded corners effect
well, for this effect we can use two approaches, the first is very simple, the second is more complex (and boring), let's see how we can make that.
Solution 1: using floating boxes in each corner
using same code above for the .btn
class, we can use the ::before
and ::after
:
.btn::before {
content: "";
width: 24px;
height: 24px;
position: absolute;
bottom: -14px;
left: -13px;
background-color: var(--yellow-color);
border-top: 2px solid var(--black-color);
transform: rotate(45deg);
}
.btn::after {
content: "";
width: 24px;
height: 24px;
position: absolute;
top: -14px;
right: -13px;
background-color: var(--yellow-color);
border-bottom: 2px solid var(--black-color);
transform: rotate(45deg);
}
Explaining: in the .btn::before
we created a box of 24px
of width and height, then using position: absolute;
to put it in the left bottom of our button, we added a border-top
of 2px
and a background-color
yellow, so the border of the button will not appear behind our boxes. the same was applied in .btn::after
, we just changed the position to top right and the border to bottom.
after all this, our button should look like this:
it looks cool, but the background color of our floating boxes is visible, the only way to hide it is to set a background color on the body with the same color as the button. 😔
Solution 2: using CSS clip-path
I confess, I found out that I suck at clip-path
, it really took me a while to understand it, so using this approach we have some pros and cons:
Pros
- you don't need floating boxes and set a background-color on body
- clip-path works like a charm (if you know how to use this nightmare)
Cons
- you can't set borders on elements clipped (it sucks)
- even trying put borders with
::before
and::after
not work (everything in the clipped area is hidden)
Now using clip-path
, we have to modify our .btn
class a bit, to set the borders i have to set a fixed width and height at the parent element: <button>
then add a children element with the class .btn__content
, with our yellow background, while the parent has black, this is a workaround to simulate a border.
.btn {
width: 230px;
height: 60px;
border: 0;
outline: none;
background-color: var(--black-color);
cursor: pointer;
position: relative;
font-family: Tomorrow, sans-serif;
font-size: .85rem;
text-transform: uppercase;
color: var(--black-color);
clip-path: polygon(92% 0, 100% 25%, 100% 100%, 8% 100%, 0% 75%, 0 0);
}
.btn__content {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 2px;
left: 2px;
right: 2px;
bottom: 2px;
background-color: var(--yellow-color);
clip-path: polygon(92% 0, 100% 25%, 100% 100%, 8% 100%, 0% 75%, 0 0);
}
we can make a default width and height and add classes to change the size of the button like: btn--small
, btn--large
...
Final touch
to add more details to our button, let's just add that little label in the corner of our button:
HTML
<button class="btn">
<div class="btn__content">
The future is now_
</div>
<span class="btn__label">r25</span>
</button>
CSS
.btn__label {
height: 10px;
font-size: .40rem;
position: absolute;
bottom: -4px;
right: 8%;
padding: 0 5px;
background-color: var(--yellow-color);
z-index: 3;
}
below i show you the same button with the class btn--secondary
, this changes the bg color to red.
Hover with glitch effect
to reproduce the glitch effect, again we use clip-path
with transform, basically we just show the button sliced in different sizes in each step of the animation, after that we call this animation inside our :hover
, this is applied to the btn__content::after
and btn__glitch
(you have to add more one <span>
inside our button.
<button class="btn">
<span class="btn__content">Get your copy now_</span>
<span class="btn__glitch"></span>
<span class="btn__label">r25</span>
</button>
and this is the hover with the glitch element being styled:
.btn__glitch {
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--yellow-color);
filter: drop-shadow(-2px 3px #67e3f3) drop-shadow(-1px -3px #02d8f3) drop-shadow(2px 1px #02d8f3);
}
.btn--secondary .btn__glitch {
background-color: var(--red-color);
}
.btn:hover .btn__glitch,
.btn:hover .btn__content::after,
.btn:focus .btn__glitch,
.btn:focus .btn__content::after {
display: block;
animation: glitch-animation 2s linear 0s infinite;
}
/* secret trick */
@keyframes glitch-animation {
0% {
opacity: 1;
transform: translateZ(0);
clip-path: polygon(0 2%, 100% 2%, 100% 5%, 0 5%);
}
2% {
clip-path: polygon(0 78%, 100% 78%, 100% 100%, 0 100%);
transform: translate(-5px);
}
6% {
clip-path: polygon(0 78%, 100% 78%, 100% 100%, 0 100%);
transform: translate(5px);
}
8% {
clip-path: polygon(0 78%, 100% 78%, 100% 100%, 0 100%);
transform: translate(-5px);
}
9% {
clip-path: polygon(0 78%, 100% 78%, 100% 100%, 0 100%);
transform: translate(0);
}
10% {
clip-path: polygon(0 54%, 100% 54%, 100% 44%, 0 44%);
transform: translate3d(5px, 0, 0);
}
13% {
clip-path: polygon(0 54%, 100% 54%, 100% 44%, 0 44%);
transform: translateZ(0);
}
13.1% {
clip-path: polygon(0 0, 0 0, 0 0, 0 0);
transform: translate3d(5px, 0, 0);
}
15% {
clip-path: polygon(0 60%, 100% 60%, 100% 40%, 0 40%);
transform: translate3d(5px, 0, 0);
}
20% {
clip-path: polygon(0 60%, 100% 60%, 100% 40%, 0 40%);
transform: translate3d(-5px, 0, 0);
}
20.1% {
clip-path: polygon(0 0, 0 0, 0 0, 0 0);
transform: translate3d(5px, 0, 0);
}
25% {
clip-path: polygon(0 85%, 100% 85%, 100% 40%, 0 40%);
transform: translate3d(5px, 0, 0);
}
30% {
clip-path: polygon(0 85%, 100% 85%, 100% 40%, 0 40%);
transform: translate3d(-5px, 0, 0);
}
30.1% {
clip-path: polygon(0 0, 0 0, 0 0, 0 0);
}
35% {
clip-path: polygon(0 63%, 100% 63%, 100% 80%, 0 80%);
transform: translate(-5px);
}
40% {
clip-path: polygon(0 63%, 100% 63%, 100% 80%, 0 80%);
transform: translate(5px);
}
45% {
clip-path: polygon(0 63%, 100% 63%, 100% 80%, 0 80%);
transform: translate(-5px);
}
50% {
clip-path: polygon(0 63%, 100% 63%, 100% 80%, 0 80%);
transform: translate(0);
}
55% {
clip-path: polygon(0 10%, 100% 10%, 100% 0, 0 0);
transform: translate3d(5px, 0, 0);
}
60% {
clip-path: polygon(0 10%, 100% 10%, 100% 0, 0 0);
transform: translateZ(0);
opacity: 1;
}
60.1% {
clip-path: polygon(0 0, 0 0, 0 0, 0 0);
opacity: 1;
}
to {
clip-path: polygon(0 0, 0 0, 0 0, 0 0);
opacity: 1;
}
}
that's it! the final code you can see in this gist.
i made this pen at codepen too, check it out!
Bonus
you can add prefers-reduced-motion
if you are concerned with accessibility:
@media (prefers-reduced-motion: reduce) {
.btn:hover .btn__glitch,
.btn:hover .btn__content::after,
.btn:focus .btn__glitch,
.btn:focus .btn__content::after {
display: none;
animation: none;
}
}
thanks for the tip Karen!
This is really cool! Thanks for posting this write-up!
Have you considered adding a prefers-reduced-motion
media query for folks that are motion sensitive?
Great work and thanks for sharing!
Top comments (7)
Wow, such a breathtaking button! 😄 😄
You're breathtaking! Everyone here is breathtaking!
Leave the buttons alone and get me the game!!!
This is really cool! Thanks for posting this write-up!
Have you considered adding a
prefers-reduced-motion
media query for folks that are motion sensitive?Great work and thanks for sharing!
That's one of the sickest animations I've seen, congrats mano.
Also, do you have any resource so I can learn better @keyframe animations? Most code I write are using like opacity 0 to 1 and translate3d lol.
Great work!
Nice one , wonderfull button
Very cool and creative use for ::before and ::after selectors!