<article class="wrapper">

  <h1>Accessible Floating <code>label</code>s</h1>

  <form action="/" method="GET" class="form">

    <label for="name" class="form__label">
      <span class="form__label-text">Your Name</span>
      <input type="text" name="name" id="name" placeholder="Your Name" autocomplete="name" class="form__text-input">
      <!-- <span class="form__error-text">Your name cannot be empty…</span> -->
    </label>
    <label for="phone" class="form__label">
      <span class="form__label-text">Phone</span>
      <input type="tel" name="phone" id="phone" placeholder="Phone (555-555-5555)" autocomplete="tel" class="form__text-input">
      <!-- <span class="form__error-text">Phone cannot be empty…</span> -->
    </label>
    <label for="message" class="form__label">
      <span class="form__label-text">Message</span>
      <textarea name="message" id="message" placeholder="Message, what's on your mind?" class="form__text-input form__textarea"></textarea>
      <!-- <span class="form__error-text">Message cannot be empty…</span> -->
    </label>
    <button class="form__btn">Submit</button>
  </form>

  <aside class="note">
    <h2>Note:</h2>
    <p>This demo exists to help provide an <strong>accessible</strong> solution for "floating <code>label</code>s" if a design calls for such a requirement.</p>
    <p>For the best usability results, it's recommended to <em>not</em> hide/float form <code>label</code>s. Only do so if you <em>absolutely must</em>. If possible, talk with your designer and come to an agreement on <code>label</code> styling.</p>
    <p>See the article "<a href="https://medium.com/simple-human/floating-labels-are-a-bad-idea-82edb64220f6">Floating labels are problematic</a>" for more details on why this approach is not recommended.</p>
  </aside>

</article>
.form {
  margin: 0 auto;
  max-width: 15rem;
}

.form__label-text {
  display: inline-block;
  padding: 0.5rem;

  .js & {
    opacity: 0;
    transform: translateY(50%);
    transition: all 0.2s ease-in-out;

    &--floating {
      opacity: 1;
      transform: translateY(15%);
    }
  }
}

.form__text-input {
  border: solid Silver 1px;
  padding: 0.5rem;
  width: 100%;

  &::placeholder {
    color: #767676;
  }
}

.form__textarea {
  min-height: 5rem;
  width: 100%;
}

.form__btn {
  background-color: Crimson;
  border: 0;
  border-radius: 0.15rem;
  color: White;
  margin: 1.5rem 0;
  padding: 0.5rem 1rem;
}

label,
textarea {
  margin: 0;
}
View Compiled
(() => {
  const classes = {
    textInput: ".form__text-input",
    labelText: ".form__label-text",
    labelTextFloating: "form__label-text--floating"
  };

  const events = {
    blur: "blur",
    keyUp: "keyup"
  };

  // Collect all text input controls
  const textInputs = document.querySelectorAll(classes.textInput);

  // Let's float the label when the checked conditions are met
  // for each input event listener
  const floatLabel = e => {
    const eventType = e.type;
    const textInput = e.target;
    const labelText = textInput.parentNode.querySelector(classes.labelText);

    // On `keyup`, if there's text in the input, show the label
    if (eventType === events.keyUp && textInput.value.length !== 0) {
      labelText.classList.add(classes.labelTextFloating);
    }

    // On `blur`, if there's no text in the input, hide the label
    if (eventType === events.blur && textInput.value.length === 0) {
      labelText.classList.remove(classes.labelTextFloating);
    }
  };

  // Add `keyup` and `blur` events for each input
  for (const textInput of textInputs) {
    textInput.addEventListener(events.keyUp, floatLabel);
    textInput.addEventListener(events.blur, floatLabel);
  }
})();
View Compiled

External CSS

  1. https://svinkle.github.io/css/codepen.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.23.0/polyfill.js
  2. https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.28/webfontloader.js
  3. https://svinkle.github.io/js/codepen.js