DEV Community

Cover image for How to Build a Glass Contact Form with HTML, CSS & JavaScript
CodeRipple
CodeRipple

Posted on

How to Build a Glass Contact Form with HTML, CSS & JavaScript

Modern contact forms don't have to feel boring. With HTML, CSS, and a little JavaScript, we can turn a standard form into a modern glass-style component with floating labels, smooth interactions, validation, and a success state.

In this tutorial, we'll look at the main ideas behind building a Glass Contact Form using HTML, CSS, and vanilla JavaScript.

What We're Building

The component starts with a simple Contact Us button.

When the user clicks it:

  • The Contact Us button disappears
  • A glass-style contact card opens
  • Users can enter their name, email, and message
  • Native browser validation checks the required fields
  • A success message appears after a valid submission
  • The form can be closed and reopened
  • The Escape key can also close the panel

No framework or external JavaScript library is required.

HTML Structure

The component begins with a wrapper containing the trigger button and expandable contact panel.

<div class="cr-popup-contact">

  <button
    class="cr-popup-contact__trigger"
    type="button"
    aria-expanded="false"
  >
    Contact Us
    <span>↗</span>
  </button>

  <div
    class="cr-popup-contact__panel"
    aria-hidden="true"
  >

    <div class="cr-popup-contact__card">

      <button
        class="cr-popup-contact__close"
        type="button"
        aria-label="Close contact form"
      >
        ×
      </button>

      <div class="cr-popup-contact__content">

        <span class="cr-popup-contact__eyebrow">
          GET IN TOUCH
        </span>

        <h2>Let's start a conversation</h2>

        <p>
          Have a question or an idea?
          Send us a message and we'll get back to you.
        </p>

      </div>

    </div>

  </div>

</div>
Enter fullscreen mode Exit fullscreen mode

Keeping the trigger and panel inside the same component wrapper makes the component easier to reuse and keeps its behavior isolated.

Creating the Glass UI

The main contact card uses a dark layered background, subtle border, rounded corners, and deep shadows.

.cr-popup-contact__card {
  position: relative;

  width: min(100%, 460px);

  border: 1px solid rgba(148, 163, 184, 0.18);
  border-radius: 22px;

  color: #f8fafc;

  background:
    linear-gradient(
      145deg,
      #111827,
      #070c1a
    );

  box-shadow:
    0 28px 65px rgba(0, 0, 0, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
Enter fullscreen mode Exit fullscreen mode

Cyan and purple blurred elements can then be positioned around the card to create the glowing glass appearance without relying on background images.

The result is a lightweight interface that still has plenty of visual depth.

Floating Form Labels

Instead of keeping labels permanently above each field, we can place them inside the inputs and move them when a field receives focus or contains a value.

.cr-popup-contact__field {
  position: relative;
}

.cr-popup-contact__field label {
  position: absolute;

  top: 15px;
  left: 14px;

  color: #7f8ba0;

  font-size: 13px;

  pointer-events: none;

  transition:
    top 0.2s ease,
    color 0.2s ease,
    font-size 0.2s ease;
}

.cr-popup-contact__field input:focus + label,
.cr-popup-contact__field input:not(:placeholder-shown) + label,
.cr-popup-contact__field textarea:focus + label,
.cr-popup-contact__field textarea:not(:placeholder-shown) + label {
  top: 6px;

  color: #22d3ee;

  font-size: 9px;
  font-weight: 700;
}
Enter fullscreen mode Exit fullscreen mode

The :placeholder-shown selector lets the label remain in its floating position after the user has entered a value.

Adding Focus Effects

A subtle cyan focus state makes it easier to see which field is currently active.

.cr-popup-contact__field input:focus,
.cr-popup-contact__field textarea:focus {
  border-color: rgba(34, 211, 238, 0.68);

  background: rgba(4, 12, 28, 0.82);

  box-shadow:
    0 0 0 3px rgba(34, 211, 238, 0.06),
    0 0 18px rgba(34, 211, 238, 0.05);
}
Enter fullscreen mode Exit fullscreen mode

This also complements the cyan and purple visual theme of the card.

Opening the Contact Form

JavaScript controls the component state by adding an is-open class to the panel.

const trigger = document.querySelector(
  ".cr-popup-contact__trigger"
);

const panel = document.querySelector(
  ".cr-popup-contact__panel"
);

trigger.addEventListener("click", () => {
  trigger.style.display = "none";

  trigger.setAttribute(
    "aria-expanded",
    "true"
  );

  panel.classList.add("is-open");

  panel.setAttribute(
    "aria-hidden",
    "false"
  );
});
Enter fullscreen mode Exit fullscreen mode

Once the form opens, the original Contact Us button is hidden so the interface stays clean.

Closing the Form

The close button reverses the state and brings the Contact Us button back.

const closeButton = document.querySelector(
  ".cr-popup-contact__close"
);

closeButton.addEventListener("click", () => {
  panel.classList.remove("is-open");

  panel.setAttribute(
    "aria-hidden",
    "true"
  );

  trigger.setAttribute(
    "aria-expanded",
    "false"
  );

  trigger.style.display = "inline-flex";
});
Enter fullscreen mode Exit fullscreen mode

Using the same state for the visual panel and ARIA attributes keeps the interaction more predictable.

Adding Keyboard Support

Users can also close the form using the Escape key.

document.addEventListener("keydown", (event) => {
  if (
    event.key === "Escape" &&
    panel.classList.contains("is-open")
  ) {
    panel.classList.remove("is-open");

    panel.setAttribute(
      "aria-hidden",
      "true"
    );

    trigger.setAttribute(
      "aria-expanded",
      "false"
    );

    trigger.style.display = "inline-flex";
  }
});
Enter fullscreen mode Exit fullscreen mode

It's a small interaction, but it makes the component more comfortable for keyboard users.

Form Validation and Success State

For the demo, we can use the browser's native validation instead of building a custom validation library.

const form = document.querySelector(
  ".cr-popup-contact__form"
);

const success = document.querySelector(
  ".cr-popup-contact__success"
);

form.addEventListener("submit", (event) => {
  event.preventDefault();

  if (!form.checkValidity()) {
    form.reportValidity();
    return;
  }

  form.reset();

  success.classList.add("is-visible");

  setTimeout(() => {
    success.classList.remove("is-visible");
  }, 3000);
});
Enter fullscreen mode Exit fullscreen mode

After a valid submission, the fields reset and a temporary success message appears.

The current component is a front-end demonstration. It does not actually send an email.

For a production project, the submit handler can be connected to your own API, WordPress handler, serverless function, or another backend service.

Responsive Design

The card uses a fluid width with a maximum size:

.cr-popup-contact__card {
  width: min(100%, 460px);
}
Enter fullscreen mode Exit fullscreen mode

We can then reduce padding and typography on smaller screens.

@media (max-width: 600px) {

  .cr-popup-contact {
    padding: 30px 12px;
  }

  .cr-popup-contact__panel.is-open
  .cr-popup-contact__card {
    padding: 26px 17px 18px;
  }

  .cr-popup-contact__content h2 {
    font-size: 24px;
  }

}
Enter fullscreen mode Exit fullscreen mode

This keeps the component usable without creating horizontal overflow on mobile devices.

Reduced Motion Support

Animated interfaces should also respect the user's motion preferences.

@media (prefers-reduced-motion: reduce) {

  .cr-popup-contact__trigger,
  .cr-popup-contact__panel,
  .cr-popup-contact__card,
  .cr-popup-contact__close,
  .cr-popup-contact__submit {
    transition: none;
  }

}
Enter fullscreen mode Exit fullscreen mode

Users who have reduced motion enabled will get the same functionality without unnecessary transitions.

Final Result

The finished Glass Contact Form combines:

  • Glass-style dark UI
  • Cyan and purple glow effects
  • Floating form labels
  • Smooth open and close interactions
  • Responsive layout
  • Native form validation
  • Success feedback
  • Escape-key support
  • Reduced-motion support
  • Vanilla HTML, CSS, and JavaScript

The component can also be customized with different colors, fields, dimensions, animations, or a real form submission backend.

Try the Complete Glass Contact Form

The snippets above explain the main techniques behind the component.

For the live demo, complete HTML, CSS and JavaScript, and copy-ready source code, visit the full CodeRipple tutorial:

👉 https://getcoderipple.com/glass-contact-form/

You can also open the component in the CodeRipple Playground to test the live preview and customize the code for your own project.

Top comments (0)