Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> CSS

Cách tạo form dọc (stacked form) với CSS

Giới thiệu

Stacked form (form dọc) hiển thị từng trường một hàng, label ở trên input. Đây là bố cục mặc định, phù hợp mobile, form đơn giản (đăng nhập, đăng ký, tìm kiếm). CSS tập trung vào spacing, focus state, và responsive width.

Ví dụ hoàn chỉnh - Form đăng nhập/đăng ký Stacked

<!DOCTYPE html>
<html lang="vi">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Stacked Form</title>
  <style>
    * { box-sizing: border-box; }
    body { font-family: 'Segoe UI', system-ui, sans-serif; background: #f5f5f5; padding: 40px 20px; }
    .form-container {
      max-width: 440px;
      margin: 0 auto;
      background: #fff;
      border-radius: 12px;
      box-shadow: 0 4px 20px rgba(0,0,0,0.08);
      padding: 40px;
    }
    h1 { text-align: center; margin: 0 0 24px; font-size: 24px; color: #1a1a1a; }
    .form-group { margin-bottom: 20px; }
    label {
      display: block;
      margin-bottom: 8px;
      font-weight: 500;
      font-size: 14px;
      color: #333;
    }
    .input-wrapper { position: relative; }
    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="tel"] {
      width: 100%;
      padding: 14px 16px;
      font-size: 16px; /* Tránh zoom iOS */
      border: 1px solid #ddd;
      border-radius: 8px;
      background: #fff;
      transition: border-color 0.2s, box-shadow 0.2s;
    }
    input:focus {
      outline: none;
      border-color: #1eadef;
      box-shadow: 0 0 0 3px rgba(30, 173, 239, 0.15);
    }
    input::placeholder { color: #aaa; }
    .toggle-password {
      position: absolute;
      right: 12px;
      top: 50%;
      transform: translateY(-50%);
      background: none;
      border: none;
      color: #888;
      cursor: pointer;
      padding: 4px;
      font-size: 14px;
    }
    .toggle-password:hover { color: #333; }
    .form-options {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin: 16px 0 24px;
      font-size: 14px;
    }
    .checkbox-wrapper {
      display: flex;
      align-items: center;
      gap: 8px;
      cursor: pointer;
    }
    .checkbox-wrapper input { width: 18px; height: 18px; accent-color: #1eadef; }
    .forgot-link { color: #1eadef; text-decoration: none; font-weight: 500; }
    .forgot-link:hover { text-decoration: underline; }
    .btn-submit {
      width: 100%;
      padding: 14px;
      font-size: 16px;
      font-weight: 600;
      color: #fff;
      background: #1eadef;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      transition: background 0.2s, transform 0.1s;
    }
    .btn-submit:hover { background: #1598d3; }
    .btn-submit:active { transform: scale(0.98); }
    .btn-submit:focus-visible { outline: 3px solid rgba(30, 173, 239, 0.4); outline-offset: 2px; }
    .signup-prompt {
      text-align: center;
      margin-top: 24px;
      font-size: 14px;
      color: #666;
    }
    .signup-prompt a { color: #1eadef; font-weight: 600; text-decoration: none; }
    .signup-prompt a:hover { text-decoration: underline; }
    @media (max-width: 480px) {
      .form-container { padding: 24px; }
    }
  </style>
</head>
<body>
  <div class="form-container">
    <h1>Đăng nhập</h1>
    <form>
      <div class="form-group">
        <label for="email">Email</label>
        <div class="input-wrapper">
          <input type="email" id="email" name="email" placeholder="you@example.com" required autocomplete="email">
        </div>
      </div>
      <div class="form-group">
        <label for="password">Mật khẩu</label>
        <div class="input-wrapper">
          <input type="password" id="password" name="password" placeholder="••••••••" required autocomplete="current-password">
          <button type="button" class="toggle-password" aria-label="Hiện/ẩn mật khẩu"><i class="fas fa-eye"></i></button>
        </div>
      </div>
      <div class="form-options">
        <label class="checkbox-wrapper">
          <input type="checkbox" name="remember"> Nhớ đăng nhập
        </label>
        <a href="#" class="forgot-link">Quên mật khẩu?</a>
      </div>
      <button type="submit" class="btn-submit">Đăng nhập</button>
    </form>
    <p class="signup-prompt">Chưa có tài khoản? <a href="#">Đăng ký ngay</a></p>
  </div>
  <script>
    // Toggle password visibility
    document.querySelectorAll('.toggle-password').forEach(btn => {
      btn.addEventListener('click', () => {
        const input = btn.previousElementSibling;
        const type = input.type === 'password' ? 'text' : 'password';
        input.type = type;
        btn.innerHTML = type === 'password' ? '<i class="fas fa-eye"></i>' : '<i class="fas fa-eye-slash"></i>';
      });
    });
  </script>
</body>
</html>

Đặc điểm Stacked Form

  • Label trên, input dưới: Dễ đọc, phù hợp màn hình hẹp.
  • Width 100%: Input luôn full container.
  • Spacing đều đặn: margin-bottom trên form-group (20-24px).
  • Focus ring rõ ràng: Quan trọng cho accessibility.

So sánh: Stacked vs Inline vs Grid

Loại Phù hợp Đặc điểm
StackedĐăng nhập, đăng ký, search, mobileLabel trên, input dưới, 1 cột
InlineToolbar, filter bar, header searchLabel + input ngang hàng, wrap trên mobile
Grid/Multi-colForm dài, checkout, settings2+ cột trên desktop, stacked mobile