Как сверстать форму регистрации

Эта статья основана на одном из практических заданий verstaem.online.

Вы можете читать материал как обычное руководство или сначала попробовать выполнить задание самостоятельно, а затем сравнить свое решение с подробным разбором.

Перейти к заданию

В этом уроке мы создадим страницу регистрации пользователя. На практике разберем семантическую форму, научимся выстраивать поля в несколько колонок, оформим чекбокс с согласием на обработку данных и адаптируем интерфейс для мобильных устройств.

Создаем HTML-разметку

Сначала создадим базовую HTML-разметку с помощью Emmet. Для этого достаточно ввести ! и нажать Tab. Обратите внимание на атрибут lang. Поскольку основной контент страницы написан на русском языке, здесь используем значение ru.

<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Форма регистрации</title>
  </head>
  <body>
  </body>
</html>

Итак, делаем форму. Добавляем собственно тег <form>, и не забываем добавить два атрибута — action и method. В первый мы вставим ссылку из задания https://postman-echo.com/post, а во второй вставим POST. Так мы сможем проверить, все ли правильно улетает из нашей формы.

Далее добавим заголовок и описание. А вот ниже пойдут поля формы. У полей есть подписи сверху, поэтому правильно будет эти подписи связать с инпутом. Можно сделать эту подпись тегом <label>, и связать с полем через атрибут for, но как по мне, просто обернуть и текст, и поле в <label> гораздо проще. Так и поступим. И обязательно укажем все важные атрибуты для поля ввода. Правильный type, заполненный name, placeholder и required.

<label class="content__label">
  <span class="content__caption">Имя</span>
  <input
    type="text"
    name="first_name"
    placeholder="Иван"
    class="content__input"
    required
  />
</label>
<label class="content__label">
  <span class="content__caption">Фамилия</span>
  <input
    type="text"
    name="last_name"
    placeholder="Соколов"
    class="content__input"
    required
  />
</label>
<label class="content__label content__label--full">
  <span class="content__caption">E-mail</span>
  <input
    type="email"
    name="email"
    placeholder="ivan@example.com"
    class="content__input"
    required
  />
</label>
<label class="content__label">
  <span class="content__caption">Пароль</span>
  <input
    type="password"
    name="password"
    placeholder="*******"
    class="content__input"
    required
  />
</label>
<label class="content__label">
  <span class="content__caption">Повторите пароль</span>
  <input
    type="password"
    name="password_confirm"
    placeholder="*******"
    class="content__input"
    required
  />
</label>

Здесь важный момент — это дополнительный класс для лейбла с email --full, чтобы это поле встало на всю ширину. А атрибут name нужен, если мы хотим, чтобы из нашей формы вообще что-то отправилось. Без него поле вообще не войдет в «отправляемые». required мы добавили, чтобы поля были обязательными, ведь вход не может быть произведен, если не введена почта и пароль.

Далее у нас чекбокс. Чекбокс делаем также в лейбле, и сразу называем его .custom-checkbox, т. к. чекбоксы не могут стилизоваться обычным методом. Нам придется хитрить, но про это в разделе CSS.

<div class="content__addition">
  <label class="custom-checkbox">
    <input type="checkbox" name="accept_terms" class="custom-checkbox__input" required />
    <span class="custom-checkbox__text">Я принимаю условия использования сервиса и политику обработки персональных данных.</span>
  </label>
</div>

Ну и остается кнопка.

<button class="content__btn" type="submit">Создать аккаунт</button>

Всем полям в форме я добавил required, чтобы форма отправлялась только тогда, когда заполнены все.

Добавляем стили

Подключим файл стилей через <link> и зададим несколько базовых правил. Для этого задания достаточно переопределить box-sizing, сбросить отступ у <body> и подключить шрифт Segoe UI, который используется в макете.

* {
  box-sizing: border-box;
}

@font-face {
  font-family: "Segoe UI";
  src: url("../fonts/SegoeUI.woff2");
  font-display: swap;
  font-weight: 400;
}

@font-face {
  font-family: "Segoe UI";
  src: url("../fonts/SegoeUIBold.woff2");
  font-display: swap;
  font-weight: 700;
}

body {
  margin: 0;
  padding: 20px;
  background: #f4f1ea;
  font-family: "Segoe UI", sans-serif;
}

Сначала делаем стили самой форме, а также заголовку и описани.

.form {
  margin: 0 auto;
  max-width: 620px;
  border-radius: 32px;
  padding: 40px;
  background: #fffdf9;
}

.content__title {
  margin: 0;
  margin-bottom: 16px;
  font-weight: 700;
  font-size: 40px;
  line-height: 120%;
  color: #232323;
}

.content__descr {
  margin: 0;
  margin-bottom: 40px;
  max-width: 448px;
  font-weight: 400;
  font-size: 16px;
  line-height: 120%;
  color: rgba(111, 106, 100, 0.92);
}

Далее расставляем элементы внутри формы. Используем flexbox вместе с flex-wrap: wrap. А вот лейблам, помимо flexbox для отступа между текстом и инпутом, зададим еще и ширину. Но не обычную, а через calc. Мы точно рассчитываем 50% — 8px. 8 потому что общий отступ 16. И здесь же сразу указываем 100% ширины у класса --full.

.content__items {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.content__label {
  display: flex;
  flex-direction: column;
  width: calc(50% - 8px);
  gap: 8px;
}

.content__label--full {
  width: 100%;
}

У инпутов нам нужны будут отступы и слева, и справа, чтобы контент не лип к краям, также нужна будет высота из макета. И конечно, не забудем указать font-family: inherit, иначе шрифт не применится. Также мы добавим псевдокласс ::placeholder, который позволит нам стилизовать цвет текста, который мы видим еще до ввода своего.

.content__caption {
  font-weight: 400;
  font-size: 12px;
  line-height: 120%;
  color: #4a4641;
}

.content__input {
  border: 1px solid #e7e1d8;
  border-radius: 16px;
  padding: 0px 16px;
  height: 54px;
  font-family: inherit;
  font-weight: 400;
  font-size: 16px;
  line-height: 120%;
  color: #232323;
  background: #faf8f4;
}

.content__input::placeholder {
  color: #232323;
}

Далее блок с чекбоксом. Ему дадим flex и расставим элементы по краям. А вот сам чекбокс — это интересно. По сути, управлять всем будет <input>, но мы скроем его, заменив на свое представление.

.content__addition {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 16px;
  margin-bottom: 16px;
  gap: 20px;
}

.custom-checkbox {
  position: relative;
  max-width: 460px;
  cursor: pointer;
}

.custom-checkbox__input {
  position: absolute;
  opacity: 0;
  appearance: none;
}

.custom-checkbox__text {
  position: relative;
  display: inline-flex;
  font-weight: 400;
  font-size: 16px;
  line-height: 120%;
  color: #4a4641;
  gap: 8px;
}

.custom-checkbox__text::before {
  content: "";
  display: block;
  border: 1px solid #4a4641;
  border-radius: 4px;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
}

.custom-checkbox__text::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 5px;
  border-radius: 2px;
  width: 8px;
  height: 8px;
  background: #4a4641;
  opacity: 0;
  transition: opacity 0.3s;
}

.custom-checkbox__input:checked + .custom-checkbox__text::after {
  opacity: 1;
}

.custom-checkbox__input:focus + .custom-checkbox__text {
  outline: 1px solid #4a4641;
  outline-offset: 3px;
  border-radius: 2px;
}

Давайте разберем, что же тут сделано. Ну, во-первых, мы даем cursor: pointer для лейбла, делая его как бы кликабельным. Затем явно скрываем инпут с помощью opacity и appearance. Оно как раз и нужно, чтобы сбросить внешний вид элемента.

Далее задаем стили тексту, в том числе flexbox. И вот тут начинается магия — мы делаем псевдоэлемент ::before для текста, и рисуем с его помощью чекбокс — по сути, просто квадрат с обводкой. А с помощью ::after рисуем квадратик, который будет внутри, то есть будет обозначать, отмечен чекбокс или нет. Этот квадратик мы по умолчанию скроем с помощью opacity.

И теперь, как я и говорил, всем будет управлять скрытый <input>. Важно не скрывать его с помощью display: none, иначе он не будет работать.

.custom-checkbox__input:checked + .custom-checkbox__text::after {
  opacity: 1;
}

Здесь мы явно говорим «если чекбокс отмечен, найди следующий элемент (через +), и у него псевдоэлементу ::after задай opacity: 1„. Мы используем фишку с комбинаторными селекторами, и находим следующий за инпутом элемент через селектор „+“. Как вы понимаете, если бы структура html была другой, это бы просто не сработало. Имейте это ввиду.

Ну, а далее просто стилизуем кнопку. Важно тут не забывать давать font-family: inherit, cursor: pointer, а также задавать состояние :hover.

.content__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 20px;
  padding: 16px;
  width: 100%;
  min-height: 58px;
  font-family: inherit;
  font-weight: 700;
  font-size: 16px;
  line-height: 120%;
  color: #fff;
  background: #232323;
  cursor: pointer;
  transition: background 0.3s;
}

.content__btn:hover {
  background: #494848;
}

В результате мы получили полноценную страницу регистрации с удобным расположением полей, чекбоксом согласия и адаптивной версией для небольших устройств. Во время выполнения задания мы закрепили навыки работы с формами, Flexbox и научились создавать интерфейсы, которые часто встречаются в современных веб-приложениях.

Решение задачи

предыдущий пост следующий пост