Interactive Contact Form UI | Validation & Toast Alerts Website Template

Interactive Contact Form UI | Validation & Toast Alerts Website Template
Contact page with real-time form validation and animated toast notifications ✨ perfect for modern web projects
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bnaw — Contact</title>
<style>
  @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap');

  :root{
    --ink:#151316;
    --paper:#f0ede6;
    --card:#ffffff;
    --clay:#b5502e;
    --moss:#4a5d43;
    --red:#c2402c;
    --muted:#6b675f;
    --line:#e3ddd0;
  }

  *{box-sizing:border-box; margin:0; padding:0;}

  body{
    background:var(--paper);
    color:var(--ink);
    font-family:'Space Grotesk', sans-serif;
    min-height:100vh;
    display:flex;
    align-items:center;
    justify-content:center;
    padding:32px 16px;
  }

  .wrap{
    width:100%;
    max-width:920px;
    background:var(--card);
    border-radius:20px;
    box-shadow:0 24px 60px -30px rgba(21,19,22,0.25);
    overflow:hidden;
    position:relative;
  }

  .top{
    display:flex;
    justify-content:space-between;
    align-items:flex-start;
    padding:30px 36px;
    border-bottom:1px solid var(--line);
    gap:16px;
    flex-wrap:wrap;
  }

  .logo{ font-size:26px; font-weight:700; letter-spacing:-0.02em; }
  .logo span{ color:var(--clay); }

  .tag{
    font-family:'DM Mono', monospace;
    font-size:12px;
    color:var(--muted);
    display:flex;
    align-items:center;
    gap:8px;
  }
  .dot{
    width:7px; height:7px; border-radius:50%;
    background:var(--moss);
    animation:pulse 2s infinite;
  }
  @keyframes pulse{
    0%,100%{ opacity:1; }
    50%{ opacity:0.35; }
  }

  .grid{
    display:grid;
    grid-template-columns:1fr 1.15fr;
  }

  @media (max-width:720px){
    .grid{ grid-template-columns:1fr; }
    .info{ border-right:none !important; border-bottom:1px solid var(--line); }
  }

  .info{ padding:36px; border-right:1px solid var(--line); }

  .info h1{ font-size:26px; line-height:1.2; letter-spacing:-0.01em; margin-bottom:14px; }
  .info p.lead{ color:var(--muted); font-size:14px; line-height:1.6; margin-bottom:28px; max-width:34ch; }

  .channel{
    font-family:'DM Mono', monospace;
    font-size:13px;
    padding:14px 0;
    border-top:1px solid var(--line);
    display:flex;
    justify-content:space-between;
    align-items:center;
    gap:10px;
    transition:padding-left 0.15s ease;
  }
  .channel:last-child{ border-bottom:1px solid var(--line); }
  .channel:hover{ padding-left:4px; }

  .channel .label{ color:var(--muted); text-transform:uppercase; letter-spacing:0.06em; font-size:11px; }
  .channel a{ color:var(--ink); text-decoration:none; }
  .channel a:hover{ color:var(--clay); }

  .form-side{ padding:36px; }

  .field{ margin-bottom:20px; position:relative; }

  .field label{
    display:flex;
    justify-content:space-between;
    font-family:'DM Mono', monospace;
    font-size:11px;
    text-transform:uppercase;
    letter-spacing:0.06em;
    color:var(--muted);
    margin-bottom:8px;
  }

  .field .err-msg{
    color:var(--red);
    font-size:11px;
    text-transform:none;
    letter-spacing:0;
    opacity:0;
    transform:translateY(-2px);
    transition:opacity 0.15s ease, transform 0.15s ease;
  }
  .field.error .err-msg{ opacity:1; transform:translateY(0); }

  .field input, .field textarea{
    width:100%;
    border:none;
    border-bottom:2px solid var(--line);
    background:transparent;
    font-family:'Space Grotesk', sans-serif;
    font-size:16px;
    padding:8px 26px 8px 2px;
    color:var(--ink);
    outline:none;
    resize:none;
    transition:border-color 0.15s ease;
  }

  .field input:focus, .field textarea:focus{ border-bottom-color:var(--clay); }
  .field.error input, .field.error textarea{ border-bottom-color:var(--red); }
  .field.valid input{ border-bottom-color:var(--moss); }

  .field textarea{ min-height:96px; }

  .check-icon{
    position:absolute;
    right:2px;
    top:34px;
    font-size:14px;
    color:var(--moss);
    opacity:0;
    transition:opacity 0.15s ease;
  }
  .field.valid .check-icon{ opacity:1; }

  .submit{
    margin-top:6px;
    width:100%;
    padding:15px 20px;
    background:var(--ink);
    color:var(--paper);
    border:2px solid var(--ink);
    font-family:'Space Grotesk', sans-serif;
    font-weight:600;
    font-size:14px;
    text-transform:uppercase;
    letter-spacing:0.05em;
    cursor:pointer;
    border-radius:10px;
    transition:background 0.15s ease, color 0.15s ease, transform 0.08s ease;
  }
  .submit:hover{ background:var(--clay); border-color:var(--clay); color:#fff; }
  .submit:active{ transform:scale(0.98); }
  .submit.loading{ opacity:0.7; pointer-events:none; }

  .foot{
    border-top:1px solid var(--line);
    padding:16px 36px;
    font-family:'DM Mono', monospace;
    font-size:11px;
    color:var(--muted);
    display:flex;
    justify-content:space-between;
    flex-wrap:wrap;
    gap:8px;
  }

  .toast{
    position:absolute;
    top:20px;
    left:50%;
    transform:translate(-50%, -20px);
    background:var(--ink);
    color:var(--paper);
    font-size:13px;
    font-family:'DM Mono', monospace;
    padding:12px 20px;
    border-radius:10px;
    display:flex;
    align-items:center;
    gap:10px;
    opacity:0;
    pointer-events:none;
    transition:opacity 0.25s ease, transform 0.25s ease;
    z-index:10;
    box-shadow:0 12px 30px -10px rgba(0,0,0,0.4);
    white-space:nowrap;
  }
  .toast.show{
    opacity:1;
    transform:translate(-50%, 0);
  }
  .toast .tick{
    width:16px; height:16px;
    border-radius:50%;
    background:var(--moss);
    display:flex; align-items:center; justify-content:center;
    font-size:10px;
    flex-shrink:0;
  }
</style>
</head>
<body>

<div class="wrap">

  <div class="toast" id="toast">
    <span class="tick">✓</span>
    <span id="toastMsg">Message sent</span>
  </div>

  <div class="top">
    <div class="logo">bn<span>aw</span>.</div>
    <div class="tag"><span class="dot"></span> Usually replies within a day</div>
  </div>

  <div class="grid">
    <div class="info">
      <h1>Let's talk.</h1>
      <p class="lead">Questions, ideas, or just want to say hi — drop a line and someone from the team will get back to you.</p>

      <div class="channel"><span class="label">Email</span><a href="mailto:hello@bnaw.co">hello@bnaw.co</a></div>
      <div class="channel"><span class="label">Phone</span><a href="tel:+10000000000">+1 (000) 000-0000</a></div>
      <div class="channel"><span class="label">Studio</span><span>123 Placeholder St, Your City</span></div>
      <div class="channel"><span class="label">Hours</span><span>Mon–Fri, 9am–6pm</span></div>
    </div>

    <div class="form-side">
      <form id="contactForm" novalidate>
        <div class="field" id="nameField">
          <label for="name">Name <span class="err-msg" id="nameErr">Enter your name</span></label>
          <input type="text" id="name" name="name">
          <span class="check-icon">✓</span>
        </div>
        <div class="field" id="emailField">
          <label for="email">Email <span class="err-msg" id="emailErr">Enter a valid email</span></label>
          <input type="email" id="email" name="email">
          <span class="check-icon">✓</span>
        </div>
        <div class="field" id="messageField">
          <label for="message">Message <span class="err-msg" id="messageErr">Say a little more</span></label>
          <textarea id="message" name="message"></textarea>
        </div>
        <button type="submit" class="submit" id="submitBtn">Send message</button>
      </form>
    </div>
  </div>

  <div class="foot">
    <span>© 2026 bnaw.</span>
    <span>Replace placeholder contact details before publishing.</span>
  </div>

</div>

<script>
  const form = document.getElementById('contactForm');
  const nameField = document.getElementById('nameField');
  const emailField = document.getElementById('emailField');
  const messageField = document.getElementById('messageField');
  const nameInput = document.getElementById('name');
  const emailInput = document.getElementById('email');
  const messageInput = document.getElementById('message');
  const submitBtn = document.getElementById('submitBtn');
  const toast = document.getElementById('toast');
  const toastMsg = document.getElementById('toastMsg');

  function validateField(field, input, test){
    const ok = test(input.value.trim());
    field.classList.toggle('valid', ok && input.value.trim() !== '');
    return ok;
  }

  function showError(field){
    field.classList.add('error');
    field.classList.remove('valid');
  }
  function clearError(field){
    field.classList.remove('error');
  }

  [nameInput, emailInput, messageInput].forEach((input, i) => {
    const field = [nameField, emailField, messageField][i];
    input.addEventListener('input', () => clearError(field));
    input.addEventListener('blur', () => {
      const value = input.value.trim();
      if(!value) return;
      if(input === emailInput){
        const ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
        ok ? field.classList.add('valid') : showError(field);
      } else {
        field.classList.add('valid');
      }
    });
  });

  function showToast(message, isError){
    toastMsg.textContent = message;
    toast.querySelector('.tick').textContent = isError ? '!' : '✓';
    toast.querySelector('.tick').style.background = isError ? 'var(--red)' : 'var(--moss)';
    toast.classList.add('show');
    setTimeout(() => toast.classList.remove('show'), 3200);
  }

  form.addEventListener('submit', function(e){
    e.preventDefault();

    let valid = true;

    if(!nameInput.value.trim()){ showError(nameField); valid = false; } else { nameField.classList.add('valid'); }

    const emailVal = emailInput.value.trim();
    if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailVal)){ showError(emailField); valid = false; } else { emailField.classList.add('valid'); }

    if(!messageInput.value.trim() || messageInput.value.trim().length < 5){ showError(messageField); valid = false; }

    if(!valid){
      showToast('Please fix the highlighted fields', true);
      return;
    }

    submitBtn.classList.add('loading');
    submitBtn.textContent = 'Sending…';

    setTimeout(() => {
      submitBtn.classList.remove('loading');
      submitBtn.textContent = 'Send message';
      showToast('Message sent — we\'ll be in touch', false);
      form.reset();
      [nameField, emailField, messageField].forEach(f => f.classList.remove('valid', 'error'));
    }, 900);
  });
</script>

</body>
</html>
Scroll to Top