Search code examples
javascripthtmlcssborder

How to make a cropped border in an input?


I need to create an input with a border that will change when the input is focused.

The inputs should works in that way: When input is not focused and is empty, the label should be inside the input:

Input without focus

When user focus the input, or input is filled, the label will move up and the border should be cropped so the label is not obscured by the border.

Input with focus

I've tried with adding before element on label with background, but i realized this is not the correct way because input has not background itself and depend on the subpage there can be other background color and finally the resould looked like this:

Input with focus

Input without focus

Could you please help me with this?


Solution

  • Check out the code below. There's no way to actually crop a border, but you can construct the top border from 2 separate elements and then shrink the right element as needed - this way you don't have to worry about the flaws of the background workaround.

    let field = document.querySelector('.field');
    let input = document.querySelector('.input-field');
    let label = document.querySelector('.label');
    let border = document.querySelector('.shrink');
    
    input.addEventListener("click", () => {
        let displacement = label.offsetWidth + 14; 
      // get label width, plus hardcoded gap which should ideally be computed (the initial 10px gap + 4px to give the label some space)
      
        field.classList.add('active');
      border.style.width = 'calc(100% - ' + displacement + 'px)';
    })
    body {
        background: URL('https://www.toptal.com/designers/subtlepatterns/uploads/just-waves.png')
    }
    
    * {
      font-family: sans-serif;
    }
    
    .field {
      position: relative;
      width: auto;
      display: inline-block;
    }
    
    input {
      border: none;
      padding: 12px;
      border-radius: 10px;
      background: transparent;
      outline: none;
      border: 2px solid #000;
      border-top: none;
      position: relative;
    }
    
    .b1 {
      position: absolute;
      top: 0;
      left: 0;
      width: 10px;
      height: 8px;
      border-radius: 10px 0 0 0;
      border-top: 2px solid #000;
    }
    
    .b2 {
      content: '';
      position: absolute;
      top: 0;
      right: 0;
      width: calc(100% - 10px); /* subtract 10px to account for left corner border */
      height: 8px;
      border-radius: 0 10px 0 0;
      border-top: 2px solid #000;
    }
    
    label {
      position: absolute;
      top: 12px;
      left: 12px;
      transition: all ease-in-out .1s;
    }
    
    .field.active label {
      transform: translateY(-20px);
    }
    <div class="field">
      <div class="top-border">
        <div class="b1"></div>
        <div class="b2 shrink"></div>
      </div>
      <label class="label">test label</label>  
      <input type="text" class="input-field">
    </div>