htmlcssreactjstwitter-bootstraptailwind-css

How to apply border-radius with border-image (gradient border) on a button placed over a gradient/video background


enter image description here

.cta-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 32px;
  height: 54px;
  font: normal normal 600 16px/21px "Inter", sans-serif;
  letter-spacing: 0;
  color: #ffffff;
  text-decoration: none;
  margin-block: 40px 0px;

  border: 2px solid transparent;
  border-radius: 32px; /* Rounded corners */
  border-image: linear-gradient(
    90deg,
    #9DFF71,
    #FFD88D,
    #FF76D5,
    #55BEFF
  ) 1;

  background: transparent;
  transition: all 0.3s ease;
}

.cta-btn:hover {
  text-decoration: none;
  background: #17303949 0% 0% no-repeat padding-box;
  color: #ffffff;
}
<a href="#" class="cta-btn">
  Gradient border with transparent background
</a>

What I tried:

Using `border-image` with `border-radius`

Using background-image: linear-gradient(white, white), linear-gradient(to right, green, gold); for masking

What I expected: The gradient border should respect the rounded corners and still allow the container background (video/gradient) to show through.

What actually happened: The corners remain sharp, and masking with solid colors hides my container background.


Solution

  • Use a pseudo-element to set the gradient as a background, as suggested in 0stone0's duplicate reference. See more: Temani Afif's answer

    body {
      height: 100vw;
      background-color: #000;
      
      background: linear-gradient(
        to right bottom,
        #000,
        #a8234b,
        #921db1,
        #422cb2
      );
    }
    
    .cta-btn {
      position: relative; /* for ::before pseudo element */
      z-index: 1;
    
      display: inline-flex;
      align-items: center;
      justify-content: center;
      padding: 0 32px;
      height: 54px;
      font: normal normal 600 16px/21px "Inter", sans-serif;
      letter-spacing: 0;
      color: #ffffff;
      text-decoration: none;
      margin-block: 40px 0px;
    
      /* border: 2px solid transparent; */
      border-radius: 32px; /* Rounded corners */
    
      background: transparent;
      transition: all 0.3s ease;
    }
    
    .cta-btn::before {
      content: "";
      position: absolute;
      z-index: -1;
      inset: 0;
      padding: 2px; /* border's width */
      
      border-radius: 32px; /* Rounded corners */
      background: linear-gradient(
        90deg,
        #9DFF71,
        #FFD88D,
        #FF76D5,
        #55BEFF
      );
      
      /* It clears the inner content by content-box and only keeps the padding. Comment it out to see. */
      -webkit-mask: 
        linear-gradient(#fff 0 0) content-box, 
        linear-gradient(#fff 0 0);
      -webkit-mask-composite: xor;
              mask-composite: exclude;
    }
    
    .cta-btn:hover {
      text-decoration: none;
      background: #17303949 0% 0% no-repeat padding-box;
      color: #ffffff;
    }
    <a href="#" class="cta-btn">
      Gradient border with transparent background
    </a>