DEV Community

Cover image for How to make a Cyberpunk 2077 button with CSS
Leandro RR
Leandro RR

Posted on • Updated on

How to make a Cyberpunk 2077 button with CSS

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 .btnclass 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!



<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">

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 (8)

Collapse
 
gughog profile image
Gustavo Oliveira

Wow, such a breathtaking button! 😄 😄

Collapse
 
lipfroy profile image
Vlad Kobyzev

You're breathtaking! Everyone here is breathtaking!

Collapse
 
theodesp profile image
Theofanis Despoudis

Leave the buttons alone and get me the game!!!

Collapse
 
knpfletcher profile image
Karen Fletcher

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!

Collapse
 
henriquerulez profile image
Pedro Henrique • Edited

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!

Collapse
 
adusoft profile image
Geofrey Aduda

Nice one , wonderfull button

Collapse
 
abdisalan_js profile image
Abdisalan

Very cool and creative use for ::before and ::after selectors!