skip to Main Content

I’m working on creating a CSS button with these specific requirements:

  1. Rectangular body with smoothly rounded corners.
  2. Triangular arrow tip on the right side.
  3. The arrow tip should have a fixed width of 20px, regardless of the
    button’s overall width.
  4. The button should be flexible to accommodate text of varying lengths
    (including potential line breaks).

Here’s my current code. I’m using clip-path to create the arrow shape on the right side of the button, setting the arrow tip to a fixed 20px width from the button’s 100% width.
To avoid text overlap with the arrow, I’ve adjusted the padding accordingly.

.cta-button {
  display: inline-block;
  padding: 10px 30px 10px 10px;
  background-color: #004c97;
  color: #ffffff;
  font-family: Arial;
  font-size: 14px;
  text-align: center;
  border: none;
  cursor: pointer;
  line-height: 1.2;
  position: relative;
  clip-path: polygon( 0 0
                    , calc(100% - 20px) 0
                    , 100% 50%
                    , calc(100% - 20px) 100%
                    , 0 100%);
  border-radius: 10px;
}

This setup mostly achieves what I need, but there’s one issue: I can’t get the rounded corners effect on the arrow tip itself.
Normally, I would use the border-radius property, but since it applies to the entire div element, it doesn’t affect the shape created by clip-path. This leaves only the left corners rounded.

Short arrow

I’ve also tried using an SVG path for a rounded-corner effect. However, with this approach, the arrow tip scales along with the button size, making it more acute as the button lengthens.

Long arrow

Is there a way to:

  1. Achieve smoothly rounded corners for the whole button, including the arrow tip, and
  2. Maintain a fixed width of 20px for the arrow tip, independent of the button’s overall length?

Any solutions or workarounds to accomplish both the fixed-size arrow tip and smooth rounded corners would be very helpful! Thank you.

2

Answers


  1. If I’m not mistaken and I understand correctly what you want to achieve,
    here it is:

    /* Button styling */
    
    .cta-button {
      display: inline-block;
      padding: 10px 40px 10px 10px;
      background-color: #004c97;
      color: #ffffff;
      font-family: Arial, sans-serif;
      font-size: 16px;
      text-align: center;
      border: none;
      cursor: pointer;
      line-height: 1.2;
      border-radius: 10px;
      position: relative;
      text-decoration: none;
    }
    
    
    /* Arrow tip styling */
    
    .cta-button::after {
      content: "";
      position: absolute;
      top: 0;
      right: -20px;
      width: 20px;
      height: 100%;
      background-color: #004c97;
      clip-path: polygon(0 0, 100% 50%, 0 100%);
      border-top-right-radius: 10px;
      border-bottom-right-radius: 10px;
    }
    
    
    /* Center the button on the page */
    
    body {
      display: flex;
      align-items: center;
      justify-content: center;
      min-height: 100vh;
      background-color: #f0f0f0;
      margin: 0;
    }
    <a href="#" class="cta-button">Click Me</a>

    Hope this fits the requirements.

    Login or Signup to reply.
  2. This solution rounds every corner of the polygon clipping path, keeping the proportions of the arrow at the end regardless of character count (move the slider around to test it):

    let charAmountSlider = document.getElementById("charAmount");
    let charCount = document.getElementById("charCount");
    let ctaButton = document.getElementsByClassName("cta-button").item(0);
    charAmountSlider.addEventListener("input", () => {
      let count = charAmountSlider.value;
      charCount.innerHTML = count;
      ctaButton.innerHTML = "a".repeat(count);
    });
    .cta-wrapper {
      filter: url(#flt_tag);
    }
    
    .cta-button {
      display: inline-block;
      padding: 10px 30px 10px 10px;
      background-color: #004c97;
      color: #ffffff;
      font-family: Arial;
      font-size: 14px;
      text-align: center;
      border: none;
      cursor: pointer;
      line-height: 1.2;
      position: relative;
      clip-path: polygon( 0 0, calc(100% - 20px) 0, 100% 50%, calc(100% - 20px) 100%, 0 100%);
      text-decoration: none;
    }
    
    #control-panel {
      border: 5px solid #aaa;
      border-radius: 10px;
      background-color: #ddd;
      margin-bottom: 10px;
      width: 50%;
      padding: 20px;
      display: flex;
      flex-direction: column;
    }
    
    #control-panel * {
      margin: 0;
      padding: 0;
    }
    
    body {
      display: flex;
      align-items: center;
      justify-content: center;
      flex-direction: column;
      min-height: 100vh;
      background-color: #f0f0f0;
      margin: 0;
      padding: 0;
    }
    
    .flt_svg {
        visibility: hidden; 
        position: absolute;
        width: 0px;
        height: 0px;
    }
    <div id="control-panel">
      <p>Number of Characters (<span id="charCount">8</span>):</p>
      <input id="charAmount" type="range" min="1" max="50" value="8">
    </div>
    <div class="cta-wrapper">
    <a href="#" class="cta-button">Click Me</a>
    </div>
    <svg class="flt_svg" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <filter id="flt_tag">
          <!-- Change stdDeviation to increase/decrease the rounding of the corners -->
          <feGaussianBlur in="SourceGraphic" stdDeviation="2" result="blur" />    
          <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 19 -9" result="flt_tag" />
          <feComposite in="SourceGraphic" in2="flt_tag" operator="atop"/>
        </filter>
      </defs>
    </svg>

    Related question and answer

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search