Search code examples
cssangularmaterial-design

Material Outline Input - Creating the Notch in Angular


Hi I am trying to implement a custom material outline input that includes a notch where the label is floating.

I know there is an existing component for Angular Material but I need to have my own special customisation therefore building my own.

I have gotten this far in creating the floating but having trouble creating the actual notch. I have seen solutions where its just a white background behind the label but that wont work in mine as input background is a different colour.

Any help/strategies best to do this would be great. I am ok with using JS what ever will work as long as it be consistent across multiple browsers is the main thing. Many thanks in advance.

Here is my stackblitz https://stackblitz.com/edit/angular-u5nxde-xdq3qy?file=src%2Fapp%2Fexample.component.html,src%2Fapp%2Fexample.component.ts,src%2Fapp%2Fexample.component.css,src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.component.css

Outline expected:

Google design input outline

Code:

     HTML 
<div class="material-textfield">
      <input placeholder=" " type="text" value="test" />
      <label class="label-container">Label</label>
    </div>

CSS
    .material-textfield {
  position: relative;
}

.label-container {
  position: absolute;
  font-size: 1rem;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  background-color: transparent;
  color: gray;
  padding: 0 0.3rem;
  margin: 0 0.5rem;
  transition: 0.1s ease-out;
  transform-origin: left top;
  pointer-events: none;
}
input {
  font-size: 1rem;
  outline: none;
  border: 1px solid gray;
  border-radius: 5px;
  padding: 1rem 0.7rem;
  color: gray;
  transition: 0.1s ease-out;

  background-color: beige;
}
input:focus {
  border-color: #6200ee;
}
input:focus + .label-container {
  color: #6200ee;
  top: 0;
  transform: translateY(-50%) scale(0.9);
  border-color: #6200ee; /* Color on focus */
}

input:not(:placeholder-shown) + .label-container {
  top: 0;
  transform: translateY(-50%) scale(0.9);
  border-color: #6200ee; /* Color when input is not empty */
}

Solution

  • I've tried to reproduce the notched outline behaviour with the minimal amount of code, I've used the technique that I've described in my previous comment :

    The notched border is made out of three html elements, they are contained in an absolute flex div palced behind the actual input,

    The first name named trailing lives in a fixed padding added before the input.

    The second element named notch contains the label so that the notch is the size of the label.

    The last element named trailing take the remaining space after the label.

    And here's the implementation :

    .form-field {
      --input-pading: 16px;
      --border: solid 1px black;
      --border-radius: 4px;
    
      padding-left: var(--input-pading);
    
      min-width: 0;
      max-height: 56px;
      max-width: 248px;
    
      position: relative;
      box-sizing: border-box;
    }
    
    .form-field:focus-within {
      --border: solid 2px black;
    }
    
    .notched-outline {
      display: flex;
      position: absolute;
    
      top: 0;
      right: 0;
      left: 0;
      width: 100%;
      max-width: 100%;
      height: 100%;
    
      box-sizing: border-box;
      pointer-events: none;
    }
    
    .notched-outline .leading {
      border-radius: var(--border-radius) 0 0 var(--border-radius);
      border-left: var(--border);
      border-top: var(--border);
      border-bottom: var(--border);
      width: var(--input-pading);
    }
    
    .notched-outline .notch {
      border-bottom: var(--border);
    }
    .notched-outline .notch .label {
      position: relative;
      transform: translateY(-50%);
      padding-left: 4px;
      padding-right: 4px;
    }
    
    .notched-outline .trailing {
      border-radius: 0 var(--border-radius) var(--border-radius) 0;
      border-right: var(--border);
      border-top: var(--border);
      border-bottom: var(--border);
      width: 100%;
    }
    
    .form-field-infix {
      width: 100%;
      padding-bottom: var(--input-pading);
      padding-top: var(--input-pading);
    }
    
    .form-field-infix input {
      width: 90%;
      outline: none;
      border: none;
      background-color: transparent;
    }
    <div class="form-field">
      <div class="notched-outline">
        <div class="leading"></div>
        <div class="notch">
          <div class="label">Label</div>
        </div>
        <div class="trailing"></div>
      </div>
      <div class="form-field-infix">
        <input placeholder="test" type="text"/>
      </div>
    </div>