Как сверстать форму входа
Эта статья основана на одном из практических заданий verstaem.online.
Вы можете читать материал как обычное руководство или сначала попробовать выполнить задание самостоятельно, а затем сравнить свое решение с подробным разбором.
Перейти к заданиюВ этом уроке мы сверстаем страницу авторизации, которая встречается практически в любом веб-приложении. По пути научимся создавать семантические формы, работать с Flexbox, строить двухколоночный макет и адаптировать интерфейс под мобильные устройства.
Создаем 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>
Итак, давайте работать с контентом. Создадим блок с классом content, а внутрь положим два других — левый и правый. Как раз правый и будет нашей формой. Слева мы сделаем 4 элемента. Да, не три, как в макете, а 4. Первым элементом будет заголовок h1, которого на макете явно нет. Мы добавим его, а затем скроем с помощью visually-hidden класса. Так он останется читаем для скринридеров, но не будет виден другим пользователям.
Логотип я достану из макета как изображение и вставлю также. А ниже — заголовок и описание.
<div class="content__info">
<h1 class="visually-hidden">Верстаем Клауд</h1>
<img src="img/logo.svg" alt="Верстаем Клауд Лого" class="content__logo">
<h2 class="content__title">
Добро пожаловать обратно
</h2>
<p class="content__descr">
Продолжите обучение, отслеживайте прогресс и открывайте новые практические задания.
</p>
</div>
Справа у нас форма. Добавляем ее, и не забываем добавить два атрибута — action и method. В первый мы вставим ссылку из задания https://postman-echo.com/post, а во второй вставим POST. Так мы сможем проверить, все ли правильно улетает из нашей формы.
Далее добавим заголовок h3 (т.к. h1 и h2 уже были использованы), и описание.
<h3 class="content__subtitle">
Вход в систему
</h3>
<p class="content__subdescr">
Используйте электронную почту и пароль.
</p>
А вот ниже пойдут поля формы. У полей есть подписи сверху, поэтому правильно будет эти подписи связать с инпутом. Можно сделать эту подпись тегом <label>, и связать с полем через атрибут for, но как по мне, просто обернуть и текст, и поле в <label> гораздо проще. Так и поступим. И обязательно укажем все важные атрибуты для поля ввода. Правильный type, заполненный name, placeholder и required.
<label class="content__label">
<span class="content__caption">Электронная почта</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>
Атрибут name нужен, если мы хотим, чтобы из нашей формы вообще что-то отправилось. Без него поле вообще не войдет в «отправляемые». required мы добавили, чтобы поля были обязательными, ведь вход не может быть произведен, если не введена почта и пароль.
Далее у нас блок, в котором находится чекбокс и кнопка «забыли пароль». Чекбокс делаем также в лейбле, и сразу называем его .custom-checkbox, т. к. чекбоксы не могут стилизоваться обычным методом. Нам придется хитрить, но про это в разделе CSS.
<div class="content__addition">
<label class="custom-checkbox">
<input type="checkbox" name="remember" class="custom-checkbox__input" required>
<span class="custom-checkbox__text">Запомнить меня</span>
</label>
<button class="content__forgot" type="button">Забыли пароль?</button>
</div>
Обратите внимание, у кнопки явно задан type="button". Это сделает кнопку обычной. Если атрибут не указать, то кнопка внутри формы автоматически будет пытаться ее отправить, что нам в данном случае не нужно.
Остается кнопка, и подпись к ней. Вот у кнопки отправки мы явно добавим type="submit".
<button class="content__btn" type="submit">Войти</button>
<div class="content__txt">
Нет аккаунта? <button class="content__create" type="button">Создать профиль</button>
</div>
Добавляем стили
Подключим файл стилей через <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;
font-family: "Segoe UI", sans-serif;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
border: 0;
padding: 0;
white-space: nowrap;
clip-path: inset(100%);
clip: rect(0 0 0 0);
overflow: hidden;
}
Здесь я как раз добавил тот самый класс .visually-hidden. Посмотрите на свойства. Все они нужны, чтобы скрыть элемент внешне полностью, но при этом не скрывать от скринридеров.
Далее быстро расставим элементы на места и зададим им фон.
.content {
display: flex;
margin: 0 auto;
max-width: 1040px;
}
.content__info,
.content__form {
padding: 52px;
width: 50%;
}
.content__info {
background: #7c5cff;
}
.content__form {
background: #10162e;
}
Далее стили для внутренностей левой части. Тут просто забираем все из макета.
.content__logo {
margin-bottom: 40px;
}
.content__title {
margin: 0;
margin-bottom: 20px;
font-weight: 700;
font-size: 52px;
line-height: 120%;
color: #fff;
}
.content__descr {
margin: 0;
font-weight: 400;
font-size: 20px;
line-height: 120%;
color: rgba(255, 255, 255, 0.92);
}
Теперь текстовые элементы в правой части. Тоже берем из макета.
.content__subtitle {
margin: 0;
margin-bottom: 16px;
font-weight: 700;
font-size: 36px;
line-height: 120%;
color: #fff;
}
.content__subdescr {
margin: 0;
margin-bottom: 40px;
font-weight: 400;
font-size: 16px;
line-height: 120%;
color: rgba(255, 255, 255, 0.92);
}
Переходим к стилизации формы, к самому интересному. Лейблу сделаем flex, чтобы расставить элементы внутри. У инпута нам нужны будут отступы и слева, и справа, чтобы контент не лип к краям, также нужна будет высота из макета. И конечно, не забудем указать font-family: inherit, иначе шрифт не применится. Также мы добавим псевдокласс ::placeholder, который позволит нам стилизовать цвет текста, который мы видим еще до ввода своего.
.content__label {
display: flex;
flex-direction: column;
margin-bottom: 16px;
gap: 8px;
}
.content__caption {
font-weight: 400;
font-size: 12px;
line-height: 120%;
color: #c9d1ff;
}
.content__input {
border: none;
border-radius: 16px;
padding: 0px 16px;
height: 54px;
font-family: inherit;
font-weight: 400;
font-size: 16px;
line-height: 120%;
color: #fff;
background: #1b2344;
}
.content__input::placeholder {
color: #fff;
}
Далее блок с чекбоксом. Ему дадим flex и расставим элементы по краям. А вот сам чекбокс — это интересно. По сути, управлять всем будет <input>, но мы скроем его, заменив на свое представление.
.content__addition {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
gap: 20px;
}
.custom-checkbox {
position: relative;
cursor: pointer;
}
.custom-checkbox__input {
position: absolute;
opacity: 0;
appearance: none;
}
.custom-checkbox__text {
position: relative;
display: inline-flex;
align-items: center;
font-weight: 400;
font-size: 12px;
line-height: 120%;
color: #b9e3ff;
gap: 8px;
}
.custom-checkbox__text::before {
content: "";
display: block;
border: 1px solid #b9e3ff;
border-radius: 4px;
width: 16px;
height: 16px;
}
.custom-checkbox__text::after {
content: "";
position: absolute;
left: 5px;
top: 5px;
border-radius: 2px;
width: 8px;
height: 8px;
background: #b9e3ff;
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 #b9e3ff;
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__forgot {
border: none;
padding: 0;
font-family: inherit;
font-weight: 400;
font-size: 12px;
line-height: 120%;
color: #b9e3ff;
background: transparent;
cursor: pointer;
transition: color 0.3s;
}
.content__forgot:hover {
color: #76b6e2;
}
.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: #17b79a;
cursor: pointer;
transition: background 0.3s;
}
.content__btn:hover {
background: #76b6e2;
}
.content__txt {
margin-top: 16px;
text-align: center;
font-weight: 400;
font-size: 12px;
line-height: 120%;
text-align: center;
color: #9ea9d6;
}
.content__create {
border: none;
padding: 0;
font-family: inherit;
font-weight: 600;
font-size: 12px;
line-height: 120%;
text-align: center;
color: #9ea9d6;
background: transparent;
cursor: pointer;
transition: color 0.3s;
}
.content__create:hover {
color: #76b6e2;
}
В результате мы получили страницу авторизации с двумя независимыми колонками, семантической формой и адаптивной версией для небольших экранов. Подобные интерфейсы встречаются практически в любом веб-приложении, поэтому такой макет станет отличной практикой перед изучением более сложных страниц и компонентов.