Material design-like form animations for Semantic UI

Written by Kevin Hougasian

We decided not to use Material Design (#reasons) while re-developing our core application suite, although i really miss MD’s micro-interactions that happen in forms. Semantic UI, while it’s feature rich and easily pluggable, lacks these.

So let’s recreate them within @SemanticUI. We need to consider Semantic UIs structure, specifically, the field wrapper, and, to make this pure css, we need to leverage html5’s :valid. But every field is initially valid you say? That’s the rub, we have to trigger invalid on an empty field by making them required.

Input

<form class="ui form">
  <div class="field">
    <input type="text" required>
    <label>Email</label>
  </div>

  <div class="field">
    <input type="password" required>
    <label>Password</label>
  </div>
</form>

.ui.form {

  // Starting with .field, let's set the position of our wrapper
  .field {
    position: relative;
    width: 100%;

    &:not(.checkbox) {
      margin-top: 1rem;
      margin-bottom: 1.7rem;
    }

    // Now to position our `label` and style it more as the
    // native `placeholder`
    label {
      color: lightgrey;
      position: absolute;
      left: .7rem;
      top: .7rem;
      transition: .2s ease-in;
    }

    // Next, `input`, so it's barely visible on any background
    // excluding checkbox and we'll add textarea
    input:not([type=checkbox]),
    textarea {
      background: transparent;
      border: none;
      border-bottom: 2px solid lightgrey;
      border-radius: 0;
      padding: .7rem;
      transition: .2s ease-in;

      &:focus ~ label,
      &:valid ~ label {
        color: grey;
        font-size: 80%;
        top: -1rem;
        left: 0;
      }

      &:focus {
        border-bottom: 2px solid dodgerblue;
        outline: none;
      }
    }
  }
}

And since we added textarea

Select

<form class="ui form">
  <div class="field select">
    <select required>
      <option value=""></option>
      <option value="">This is a selected option</option>
    </select>
    <label>Select</label>
  </div>  
</form>
.ui.form {
  .field {
    position: relative;
    width: 100%;

    &:not(.checkbox) {
      margin-top: 1rem;
      margin-bottom: 1.7rem;
    }

    label {
      color: lightgrey;
      position: absolute;
      left: .7rem;
      top: .7rem;
      transition: .2s ease-in;
    }

    // Now we're adding a .select class to .field
    &.select {
      display: flex;

      &:after {
        align-self: center;
        color: lightgrey;
        content: "\f107";
        // Semantic UI sets Font Awesome family name to Icons ;)
        font-family: "Icons";
        margin-left: -1rem;
      }

      select {
        background: transparent;
        border: none;
        border-bottom: 2px solid lightgrey;
        padding: .7rem 2rem .7rem .7rem;
        position: relative;
        border-radius: 0;
        transition: .2s ease-in;
        -webkit-appearance: none;
        -moz-appearance: none;

        &:focus {
          outline: none !important;
          border-bottom: 2px solid dodgerblue !important;
        }

        &:focus ~ label,
        &:valid ~ label {
          color: gray;
          font-size: 80%;
          top: -1rem;
          left: 0;
        }
      }
    }
  }
}

{: .ui.info.message} Chrome and Safari both ignore border-radius on select elements unless -webkit-appearance is overridden to an appropriate value.

Checkbox

Now for the bonus round! And unless you’re on a -webkit or -moz browser, IOW, not IE or Edge, you just can’t do this. Ironically, IE mobile supports the feature using -webkit-appearance.

This entire CSS trick hinges on -prefix-appearance: none, and, as luck would have it, appearance has been dropped from the CSS3 spec. It will probably re-appear in CSS4-UI spec. We’ll see…anyway.

<form class="ui form">
  <div class="field checkbox">
    <input type="checkbox" required>
    <label>checkbox</label>
  </div>  
</form>
// Now we're adding .checkbox to .field
.ui.form {
  .field {
    position: relative;
    width: 100%;

    &:not(.checkbox) {
      margin-top: 1rem;
      margin-bottom: 1.7rem;
    }

    label {
      color: lightgrey;
      position: absolute;
      left: .7rem;
      top: .7rem;
      transition: .2s ease-in;
    }

    &.checkbox {
      align-items: flex-start;
      align-content: flex-start;
      display: flex;
      margin: .7rem 0;
      position: relative;

      input[type=checkbox] {
        padding: .7rem;
        border: 2px solid lightgrey;
        transition: .2s ease-in;
        -webkit-appearance: none;
        -moz-appearance: none;

        &:focus {
          outline: none;
        }

        &:before {
          color: grey;
          content: "";
          transition: .2s ease-in;
        }

        &:checked {
          padding: .6rem .35rem;
          margin: -.3rem .1rem 0 .7rem;
          border-left: none;
          border-top: none;
          transform: rotate(45deg);
        }
      }

      label {
        font-size: initial;
        position: static;
        margin-left: 1rem;
        align-self: center;
      }
    }
  }  
}

View it all on CodePen

Published August 01, 2017 by

undefined avatar
Kevin Hougasian Lead Frontend Developer

Suggested Reading