HTML Practice
Содержание
- Введение
- Кратко о GIT
- Единицы измерения в CSS
- Форматирование текста
- Многоколоночный текст
- Свойство display
- Блочная модель и box-sizing
- Позиционирование
- Псевдо-классы и псевдо-элементы
- Картинка в тексте
- CSS переменные
- Выравнивание блока по центру
- Самый правильный способ разнести блоки в разные стороны
- Горизонтальное и вертикальное центрирование блока
- Многоколоночные макеты
- Сетки
- Отзывчивый дизайн и медиа-выражения
- Градиенты, тени и фильтры
- Трансформации
- Вёрстка тултипов
- Выпадающее меню и дропдауны
- Вёрстка модальных окон
- Вёрстка чекбоксов
- Audio и Video
- Знаете ли вы?
- Практика
Введение
Это практическое руководство создано для начинающих верстальщиков и frontend-разработчиков, которые уже успели познакомиться с множеством HTML-тегов и CSS-свойств. Как минимум, вы должны быть знакомы со следующими тегами:
- A
- ARTICLE
- BODY
- BUTTON
- DIV
- H1
- H2
- H3
- HEAD
- HTML
- IMG
- INPUT
- LABEL
- LI
- LINK
- P
- SECTION
- SPAN
- TEXTAREA
- TITLE
- UL
Было бы неплохо, если бы вы могли оперировать такими единицами измерения, как px, %, em, rem, vw, vh, vmin и vmax. О единицах измерения стоит почитать здесь, но не волнуйтесь, с основными единицами измерения мы ознакомимся в скором времени. А перед тем, как вы приступите к этому руководству, вы должны понимать, как писать селекторы. Для закрепления этих знаний предлагаю поиграть в эту игру. Кроме того, вы должны знать основные css-свойства, перечисленные в списке ниже, и другие.
- background
- border
- box-shadow
- display
- clear
- cursor
- color
- column-count
- float
- font-family
- font-size
- font-weight
- height
- justify-content
- line-height
- list-style
- margin
- max-height
- max-width
- min-height
- min-width
- opacity
- outline
- overflow
- padding
- position
- text-align
- text-decoration
- text-overflow
- text-shadow
- transform
- transition
- vertical-align
- visibility
- white-space
- width
- z-index
Кратко о GIT
GIT — это система контроля версий. Позволяет отслеживать и фиксировать изменения в коде, смотреть, как развивался проект и какие люди вкладывали в него своё время. Без базовых знаний гита работать frontend-разработчиком просто нельзя.
Установка
По этой ссылке вы найдете инструкцию по установке GIT на свой компьютер: Download GIT
Настройка
После установки, нам нужно настроить GIT. Самое главное - выполнить команды:
git config --global user.name "My Name" git config --global user.email myEmail@example.com
GitHub
Теперь нужно зарегистрироваться на GitHub или на любом другом сервисе для хостинга проектов, основанном на системе контроля версий GIT.
С помощью генератора ключей нужно создать новый ключ для репозитория. Для этого воспользуйтесь командой
ssh-keygen
Следуйте инструкциям. После создания ключей поместите их в папку C:\Users\YOUR_NAME\.ssh для пользователей Windows или в папку ~/.ssh для пользователей Linux или MacOS.
На сайте GitHub.com добавьте публичный (id_rsa.pub) ssh-ключ. Это можно сделать в разделе настройки пользователя.
Создайте на сайте новый проект. После создания проекта вы найдете ссылку вида git@github.com:YOUR_LOGIN/YOUR_PROJECT.git
На своём компьютере создайте папку, где будете хранить ваши проекты. В этой папке откройте терминал (например, C:\projects) и выполните команду:
git clone git@github.com:YOUR_LOGIN/YOUR_PROJECT.git
На данном этапе можно сказать, что мы настроили GIT.
Основные команды
Чтобы сохранить измененные файлы в каком-то состоянии, нужно их закоммитить. Для этого нужно сначала добавить измененные файлы в будущий коммит
git add -A
Далее нужно сохранить коммит
git commit -m "Название коммита"
Коммиты лучше всего называть так, чтобы было понятно, что было сделано в этом коммите, как именно изменилось приложение или сайт. Например, "Изменен цвет кнопки корзины". Еще лучше было бы, если бы мы написали это на английском.
Теперь нужно отправить изменения на сервер. Для этого есть команда:
git push
Чтобы получить изменения с сервера, существует команда:
git pull
Кроме того, вам стоит изучить такие команды, как merge, status, branch, checkout, revert и многие другие.
Больше подробностей читать здесь:
Единицы измерения в CSS
Прежде, чем начать что-то делать, надо разобраться с принятыми единицами измерения.CSS поддерживает множество единиц измерения, но здесь будут описаны только самые часто-применяемые.
px - пиксели
Самая основная единица измерения, которая встречается практически везде - это пиксели. Пиксель - это точка минимального размера на вашем экране. Весь экран состоит из таких точек. Вообще, если мы говорим о CSS, то пиксели здесь не всегда будут равны тем самым точкам на экране. К примеру, если у вас Retina Display, то, скорее всего, за один CSS-пиксель у вас будет отвечать аж квадрат из 4-х соседних пикселей. Эта технология служит для сглаживания изображений. Итак, пиксели. Если вам в конкретной задаче нужна постоянная, практически ни от чего не зависящая единица измерения, смело используйте её.
Пример - нужно установить размер контейнера для сайта. С очень высокой вероятностью, вы должны использовать пиксели.
% - проценты
Проценты используются там, где нам нужно задать величину относительно другого значения. Как правило, относительно родителя.
К примеру, у родительского блока есть четыре равных, дочерних. Ширина родительского блока зависит от размера экрана пользователя и нам точно не известна,а дочерние блоки должны быть расположены рядом друг с другом. Используем ширину 25% у блоков и мы решим эту задачу.
em и rem - значение относительно шрифта.
em - это относительная единица длины, равная размеру текущего шрифта. То есть, если мы верстаем кнопку, кегль шрифта которой равен 10px,мы можем установить padding: 1.5em, что будет означать, что пока размер шрифта этой кнопки равен 10px, то padding будет равен 15px (1.5*10px). Если где-то ниже будет вторая такая кнопка с модификатором (дополнительный класс, который, например, переопределяет размер шрифта на 20px), то padding, равный 1.5em преобразуется браузером в 30px (1.5*20px).
rem делает всё то же самое, но берёт размер шрифта, установленного в html. То есть, если мы хотим точно управлять размером шрифта, лучше установить этот код:
html { font-size: 18px; }
Так, мы сможем установить размер шрифта в rem во всём документе, а когда нам понадобится его уменьшить (например, для мобильных устройств с маленькими экранами),мы просто изменим это значение прям в html и весь сайт адаптируется под новый экран.
vw, vh, vmin и vmax - значение относительно размера экрана
Тут всё просто. Каким бы ни был экран пользователя вашего сайта, 1vw всегда будет равен 1% от ширины его экрана, а 1vh будет равен 1% от высоты его экрана.
vmin - это наименьшее значение среди vw и vh. Если экран пользователя расположен горизонтально (экран ноутбука, например), то vmin = vh.Если это экран телефона, например, расположенный вертикально, то vmin = vw
vmax - точно так же, но наоборот. Это наибольшее среди значений vw и vh.
fr - (fragment) - "единица гибкости"
fr используется в гридах . Об этой единице измерения очень хорошо описано в этой статье: Что такое единица гибкости fr в CSS, доступным и простым языком . Если коротко, то fr позволяет разделять пространство на части.
К примеру, элементы грида нужно разделить так, чтобы все они были равны. Для этого нужно задать для них ширину равную, например, 1fr.Тогда все элементы будут равны между собой.
А что если у нас есть два блока в грид-контейнере, ширина которых должна относиться друг к другу как 1 к 3? Получается, всего частей у нас 4 (1+3),тогда ширина первого будет 25%, а второго 75%. И зачем же нам fr? Мы можем задать ширину первого 1fr, а второго 3fr и получить такой же результат. Но почему бы просто ни использовать проценты? А что если мы точно знаем, что первый блок должен быть 50px в ширину, а второй и третий - делить между собой оставшееся пространство так, чтобы второму досталось 25%, а третьему 75%? Если задать "50px 25% 75%", ничего не получится, потому что общая сумма будет равна 25% ширины контейнера + 75% ширины контейнера + 50px, что уже точно больше, чем 100% и блок выйдет за границу контейнера. Как раз здесь нам и нужно использовать fr. Мы можем задать "50px 1fr 3fr" - и вуаля! Ведь fr разделит именно свободное пространство.
Таким образом, fr позволяет нам делить имеющееся пространство на доли, отношение которых и задаётся этой единицей измерения.
Остальные единицы измерения
Также CSS поддерживает и другие единицы измерения:
- mm (миллиметры),
- cm (сантиметры)
- ex (относительно высоты строчной буквы)
- ch (относительно ширины строчной буквы)
- in (дюймы)
- pt (типографские пункты = 1/72in = 0.3528mm)
- pc (пики. одна пика эквивалентна 12pt)
и другие. Они значительно реже применяются на практике, но для общего развития стоит изучить и их.
Форматирование текста
Знаете ли вы, что CSS имеет средства форматирования текста не хуже чем у Microsoft Word? Вы можете задать свой шрифт, жирность, начертание, цвет, тень,размер и еще множество параметров, чтобы получить идеально отформатированный текст. Для форматирования текста используется следующий основной набор свойств:
-
font-family
Задаёт шрифт для элемента. К примеру,font-family: Arial, sans-serif
Изменяет шрифт на Arial, а если этого шрифта в системе нет, то берёт любой из установленных шрифтов семейства sans-serif. -
font-size
Задаёт кегль для текста (размер шрифта).font-size: 18px
Задаёт размер шрифта в 18px. Могут быть использованы относительные значения: "larger" и "smaller", указывающие, что размер должен быть больше или меньше стандартного значения. -
font-weight
Устанавливает насыщенность (жирность) шрифта. Значением могут быть числа от 100 до 900 с шагом 100, а также bold, bolder, lighter и normal.
Примечание: значения должны поддерживаться шрифтом! Если шрифт не поддерживает какие-то значения насыщенности, то вы не сможете задать эти значения и будет выбрано ближайшее поддерживающееся. -
font-style
Определяет начертание шрифта. Может быть обычным (normal), курсивным (italic) и наклонным (oblique).
Наклонное начертание хоть и похоже на курсивное, однако, это разные вещи. Курсивным текст станет только в том случае, если шрифт поддерживает курсивное начертание. Наклонный текст образуется путём программного наклона букв обычного шрифта. -
font-variant
Благодаря этому свойству, можно отображать текст капителью. Это означает, что строчные буквы будут отображены как прописные, но уменьшенного размера. -
letter-spacing
Определяет интервал между символами. Желательно задавать значения в em. Принимает и отрицательные значения. -
word-spacing
Устанавливает интервал между словами (ширина пробела) -
text-indent
Устанавливает ширину первой строки абзаца (красной строки) -
text-transform
Управляет регистром текста. К примеру, значение uppercase устанавливает все символы текста прописными. -
text-decoration
Добавляет подчёркивание (underline), перечёркивание (line-through) или линию над текстом (overline). Кроме того, значение none полностью убирает стандартные подчёркивания у ссылок. -
text-align
Выравнивание текста. Доступные значения:center(по центру),justify(по ширине строки),start(по левому краю, для тех языков, где текст идёт слева направо),end(по правому краю, для тех языков, где текст идёт слева направо) -
text-shadow
Добавляет тень тексту. К примеру,text-shadow: 1px 2px 3px black
, где
первое значение (1px) — сдвиг по оси x,
второе значение (2px) — сдвиг по оси y,
третье значение (3px) — радиус размытия тени,
четвертое значение (black) — цвет тени.
Кроме того, таких групп значений может быть сколько угодно. К примеру,text-shadow: 1px 2px 3px black, 0 0 1em red
-
column-count
Задаёт количество колонок в тексте. Применяется, в основном, для газетной вёрстки. -
line-height
Задаёт высоту строки. Если задавать без единиц измерения, например,line-height: 2
, значение будет воспринято как множитель.
Собственные шрифты
Для подключения собственного шрифта используется правило @font-face.
@font-face { font-family: 'Philosopher'; /* Название шрифта*/ font-style: normal; /* Начертание */ font-weight: 400; /* Насыщенность */ src: url(./fonts/philosopher.woff2) format('woff2'); /* Путь к файлу шрифта и его формат */ }
Для подключение такого шрифта к элементу, используют код:
.selector { font-family: 'Philosopher', sans-serif; }
Google Fonts
Для упрощения подключения шрифтов, используют различные библиотеки шрифтов. К примеру,
Google Fonts. Такие библиотеки содержат сотни шрифтов, а так же оптимизаторы для браузеров.
Всё что нам нужно — выбрать требуемый шрифт, выбрать, какие начертания и языки нам требуются (для русского языка Cyrillic),
подключить в<head>
стиль шрифта, имеющий примерно такой вид:
<head> ... <link href="https://fonts.googleapis.com/css?family=Philosopher:400,700&display=swap&subset=cyrillic" rel="stylesheet"> ... </head>
Многоколоночный текст
Время от времени возникает задача расположить список или текст в две колонки. Годы идут, CSS развивается, поколения
верстальщиков сменяют поколения, но свои костыли люди как делали, так и делают. Мало кто знает, но в CSS появилось
свойство
column-count, позволяющее задать количество
колонок для списка или текста. Также есть свойство
column-width, которое задаёт минимальную ширину
для колонки,
column-rule, рисующее линию между колонками
словно border,
column-gap, задающее расстояние
между колонками и
columns, объединяющее в себе
column-width и column-count.
Разберемся на примерах из жизни. Простой пример: нужно сделать так, чтобы список располагался в две колонки.
<ul> <li>Lorem</li> <li>ipsum</li> <li>dolor</li> <li>sit</li> <li>amet</li> <li>consectetur</li> <li>adipisicing</li> <li>elit</li> </ul>
Добавим следующий CSS-код:
ul { column-count: 2; }
В результате получим это:
Просто, не правда ли? А теперь посмотрим на немного более сложный пример. Итак, наши клиенты - люди с планшетами.Наше веб-приложение похоже на газету и листать её нужно слева направо, а не вниз, как остальные сайты. Текст должен располагаться в три колонки на экран. Общее количество колонок неизвестно, также, как и количество текста.
Из задания лишь понятно, что высота приложения должна быть равна высоте экрана пользователя, а за раз на экране должно быть отображено три колонки.Для начала, создадим контейнер, который бы занимал весь экран и имел прокрутку слева направо.
.overflow-container { height: 100vh; /* занимает 100% высоты экрана */ overflow-x: auto; /* показывает горизонтальный скролл в том случае, если он нужен */ overflow-y: hidden; /* скрывает вертикальный скролл даже если он нужен */ }
Теперь определим стили для текста. Мы понимаем, что так как колонки должно быть три, то каждая колонка должна иметь ширину 1/3 от ширины страницы.Это означает, что ширина колонки должна быть 100vw/3. Но число 33.333333vw не очень красиво выглядит. А свойство column-width задаёт именно минимальную ширину колонки. То есть, ширина колонки всегда будет подбираться автоматически, однако, минимальная ширина будет такой, какой мы её зададим. Давайте зададим ширину колонки в 30vw - это ровное число, которое меньше 33.3333vw.
.col-3 { column-width: 30vw; }
И добавим немного стиля. К примеру, расстояние между колонками и тонкую серую линию между ними.
.col-3 { column-width: 30vw; column-gap: 2vw; column-rule: 1px solid #ccc; }
Посмотрим, что получилось:
Запустить примерСвойство display
Одним из важнейший CSS-свойств является display. Оно позволяет переопределять тип отображения элемента, не изменяя семантику кода. Рассмотрим основные значения этого свойства:
- none — скрывает элемент;
- block — определяет элемент как "блочный", что означает, что элемент не обтекается другими объектами, а переносится на новую строку и заполняет её полностью. Следующий элемент будет отрисован под блочным элементом. Является стандартным значением элементов div, p, ul, article, section и других;
- inline — определяет элемент как "строчный", что означает, что элемент ведёт себя как текст. Является стандартным значением элементов span, a, s, b и других;
- inline-block — смесь строчного и блочного. Фактически, являясь блочным, элемент получает возможность обтекаться строчными элементами, как тег img;
- flex — превращает элемент в flex-контейнер. Это означает, что внутренние элементы выстраиваются так, как мы зададим с помощью свойств flex, flex-direction, flex-wrap, align-items, justify-content и других. Более подробно о flex-контейнерах стоит прочитать тут: Выравнивание элементов в Flex контейнере ;
- grid — превращает элемент в grid-контейнер. Гриды это новая технология построения сеток. При помощи свойств управления сетками, например, grid-template-columns, мы можем управлять отображением содержимого внутри грида. Более подробно о гридах можно почитать здесь: Основные понятия Grid Layout .
Задачи
Задачи на добавление всего одной строки кода. Смотрите HTML-код, подумайте, какое CSS-свойство спасёт ситуацию. Поиграйтесь со значениями этого свойства. Все необходимые для выполнения задачи знания описаны в этой главе.
1. Напишите CSS-код, позволяющий скрыть этот элемент
Посмотреть задание2. Не меняя HTML-кода, сделайте так, чтобы каждое предложение начиналось с новой строки
Посмотреть задание3. Сделайте так, чтобы все элементы списка шли в одну строку
Посмотреть задание4. Добавьте одно свойство так, чтобы меню стало широким
Посмотреть задание5. Сделайте так, чтобы счёт шёл слева направо, а не снизу вверх
Посмотреть задание6. Добавьте одну строку так, чтобы получилась таблица
Посмотреть заданиеБлочная модель и box-sizing
Элементы в CSS представляют собой набор слоёв: ширина и высота самого элемента (width и height), поля элемента (padding), граница элемента (border) и отступы (margin). Подробнее о блочной модели стоит почитать здесь.
<div class="box-model"> <div class="margin">margin <div class="border">border <div class="padding">padding <div class="size">width</div> </div> </div> </div> </div>
.box-model { display: flex; justify-content: center; } .box-model * { text-align: center; border: 1px dashed black; padding: 10px; margin: 10px 24px 24px; font-size: 14px; font-family: monospace; } .box-model .margin { background: lightsalmon; } .box-model .border { background: navajowhite; } .box-model .padding { background: lightgreen; } .box-model .size { background: lightblue; width: 90px; height: 80px; position: relative; padding: 0 10px; text-align: left; } .box-model .size::after { content: 'height'; position: absolute; writing-mode: vertical-lr; top: 10px; right: 0; }
Долгое время в CSS изменение padding приводило к изменению width у элементов, из-за чего два одинаковых по ширине элементас разными по ширине полями, в реальности имели разную ширину. Пример ниже демонстрирует этот недуг:
<div class="one">padding: 0</div> <div class="two">padding: 40px</div>
.one { width: 200px; height: 100px; padding: 0; } .two { width: 200px; height: 100px; padding: 40px; }
.one,.two{ background: teal; color: white; margin: 20px auto; } .two{ box-shadow: inset 0 0 0 40px darkcyan; }
Для визуализации, padding был окрашен с помощью свойства box-shadow. В действительности же, задать цвет для padding невозможно!
На помощь в борьбе с этим нелогичным, казалось бы, поведением пришел box-sizing. Добавим в наши блоки всего по одной строчке кода:
.one { width: 200px; height: 100px; padding: 0; box-sizing: border-box; } .two { width: 200px; height: 100px; padding: 40px; box-sizing: border-box; }
Так стало гораздо проще работать с CSS, поэтому многие добавляютbox-sizing: border-box всем элементам на сайте при помощи этого кода:
* { box-sizing: border-box; }
Это стало хорошей и распространённой практикой, однако по умолчанию значением свойстваbox-sizing является стандартная модель content-box, возвращающий нас в те времена, когда приходилось вычислять ширину элемента по его полям.
Позиционирование
position - это фундаментальное свойство CSS, которое позволяет определить, в каком месте браузер будет отрисовывать элементы.
Свойство position обладает следующими значениями:
- static - значение по умолчанию. Элемент будет отрисован "нормально"
- relative - позволяет отрисовать элемент "нормально", но, благодаря CSS-свойствам top, right, bottom и left,появляется возможность сместить блок из "нормальной" позиции.
- absolute - самое интересное свойство. Положение блока рассчитывается при помощи top, right, bottom и left,но не относительно изначального места, а относительно ближайшего родителя с position не равным static или элемента body.
- fixed - положение относительно границ экрана. Также рассчитывается с помощью top, right, bottom и left, но родителем является сам экран.
- sticky - "прилипает" к границам экрана в том случае, если выходит за границы экрана. Также устанавливается при помощи top, right, bottom и left.Подробнее тут: Как на самом деле работает position: sticky в CSS
Поэкспериментировать с position: absolute можно здесь:
Запустить примерПоэкспериментировать с position: sticky можно тут:
Запустить примерСмещение блока: top, right, bottom и left
По умолчанию значением этих свойств является auto. Мы можем использовать для задания положения, например, px или %.
Используются в купе с не статично спозиционированными блоками.- top смещает блок сверху вниз;
- bottom смещает блок снизу вверх;
- left смещает блок слева направо;
- right смещает блок справа налево.
Слои и z-index
Иногда, сместив один блок относительно другого, они начинают "наезжать" друг на друга. Причём, не всегда так, как мы этого хотим.Что если мы хотим, чтобы блок, который отрисовался "под" вторым блоком, был отрисован "над" ним? Для этого на помощь приходит свойство z-index. Значением этого свойства может быть просто любое число, без единиц измерения. Установив z-index: 2 для первого блока и z-index: 1 для второго, мы точно будем знать, что первый блок будет отрисован "над" вторым, так как его z-index больше. По умолчанию же, "над" отрисовывается тот блок, который в структуре HTML был ближе к концу документа.
Если вы математик, то вам будет понятно следующее объяснение: наш экран имеет две координаты: X и Y. Координаты начинаются из верхнего левого угла.X направлен вправо. Y — вниз. А теперь представим себе ещё одну координату — Z. Она направлена из той же точки, но перпендикулярно экрану, на зрителя. Это и есть z-index.
Если вы работали с фотошопом или какими-то другими графическими редакторами, то вам знакомо понятие "слои". Мы можем располагать различные изображения на этих слоях, а в случае перекрытия, показан будет тот слой, что находится выше. И это тоже z-index.
Раскомментируйте строку с z-index, чтобы понять, как работает это свойство.
Посмотреть заданиеПсевдо-классы и псевдо-элементы
Псевдо-классы и псевдо-элементы решают группу задач, связанных с выбором конкретного элемента списка или с изменением поведения объекта в связи с возникновением какого-либо события. Здесь мы разберём лишь часть из существующих псевдо-элементов и псевдо-классов, а больше вы всегда сможете узнать здесь: Псевдо-классы и псевдо-элементы
Псевдо-классы
Псевдо-классы представляют собой ключевое слово, начинающееся с двоеточия (:), которое добавляют после селектора:
selector:pseudo-class { ... }
Ниже приведён список наиболее популярных псевдо-классов
-
:hover
Срабатывает при наведении курсора на элемент -
:visited
Позволяет выбрать посещённые ссылки -
:active
Срабатывает при нажатии на элемент, но может быть применим только к элементам<a>
и<button>
-
:focus
Срабатывает при получении элементом фокуса. Работает только для элементов<a>
,<button>
,<input>
,<textarea>
-
:checked
Срабатывает для элемента<input type="checkbox">
или<input type="radio">
. Позволяет применять стили в том случае, если на элементе стоит отметка -
:disabled
Срабатывает, если элемент имеет атрибутdisabled
, к примеру, элемент<input type="..." disabled>
или<button disabled>
-
:required
Срабатывает, если элемент имеет атрибутrequired
, всегда для элемента<input required>
-
:first-child
Позволяет выбрать первый элемент в своем родителе -
:last-child
Позволяет выбрать последний элемент в своем родителе -
:nth-child
Позволяет выбирать один или более элементов, основываясь на их позиции среди группы соседних элементов.
Примеры:
ul li:nth-child(2)
выберет второй элемент
ul li:nth-child(2n)
выберет каждый второй элемент (через один)
ul li:nth-child(3n+1)
выберет 1,4,7,9 ... и так далее, через два элементы. -
:nth-last-child
Точно так же, но счёт идёт с последнего элемента к первому. К примеру, для выбора предпоследнего элемента списка:ul li:nth-last-child(2)
-
:not()
"НЕ". Это функция, принимающая селектор внутрь скобок. К примеру, нам нужно найти все ссылки, у которых нет атрибутаtarget
:
a:not([target])
Ещё пример: требуется найти все статьи, не имеющие класса.top
:
article:not(.top)
Псевдо-элементы
Псевдо-элементов значительно меньше, а отличить их можно по двойному двоеточию (::), которое добавляется к селектору.
selector::pseudo-element { ... }
Браузерами поддерживается и устаревший синтаксис с одинарным двоеточием.
Псевдо-элементы ведут себя как элементы, то есть, у них есть размеры, они могут быть спозиционированы, их видно. Псевдо-классы же мы увидеть не могли,могли лишь только увидеть их влияние на элементы. Рассмотрим псевдо-элементы подробнее:
-
::before
Создаёт псевдо-элемент, который будет являться первым потомком элемента. Не работает без свойстваcontent: '';
a::before{ content: '♥'; }
В этом примере, перед каждой ссылкой на сайте будет добавляться сердечко. -
::after
Работает точно так же, как и ::before, но псевдо-элемент создаётся в самом конце и будет являться последним потомком.
С остальными псевдо-элементами вы познакомитесь позже, на личной практике. Они используются заметно реже. Конечно, ссылки на них я приведу:
Картинка в тексте
Часто, бывает нужно вставить картинку в текст. Но работает это не всегда так, как мы это задумывали. Попробуем вставить картинку без стилей и посмотрим, что получится.
Код примера предельно прост:
<p> <img src="./assets/images/image.svg" alt="Just image"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Hic commodi maiores excepturi dolore cupiditate sit obcaecati, repellat doloribus rerum! Dicta eius aliquid eaque sed sequi obcaecati debitis quos veniam placeat. </p> <p> Itaque voluptates eum nihil minima quo iure voluptas, atque amet molestias, perspiciatis sed cupiditate nemo inventore velit soluta iste ut nesciunt! Qui fuga nesciunt accusantium magnam officiis corrupti, quibusdam amet. </p> <p> Soluta, laudantium in. Dolorem laboriosam nobis nihil iste eum sapiente maxime, facilis minus atque debitis corporis quasi, molestias delectus magni placeat ipsum iure perspiciatis doloremque repudiandae rem tenetur, in dolorum. </p>
Как видите, картинка стала частью первой строки текста. Нам редко нужно, чтобы поведение было именно таким. Чаще нам необходимо сделать так, чтобы картинка обтекалась текстом. Для этого существует свойство float.
Добавим к нашему примеру float: left и посмотрим, что получится
img { float: left; }
Здесь мы видим, что картинка стала обтекаться текстом. Правда, из-за того, что текста много, мы не видим одного подводного камня этого решения.
Давайте уберём последние два абзаца и посмотрим, что будет
Lorem ipsum dolor...
Обратите внимание, что-то пошло не так и даже этот абзац, не относящийся к примеру, всё равно обтекается картинкой. Явно не то, чего мы ожидали. Всё дело в том, что флоаты (поплавки) "всплывают" над родительским элементом, поэтому его высота больше не зависит от нашей картинки.
Для того, чтобы исправить подобное поведение, нужно воспользоваться "очисткой" — clear. Изменим наш код следующим образом:
<p> <img src="./assets/images/image.svg" alt="Just image"> Lorem ipsum dolor... </p> <div class="clear"></div>
img { float: left; } .clear { clear: both; }
Как видите, абзац, который вы сейчас читаете, находится уже за пределами примера. Добавив такой чистящий элемент в самый конец родительского контейнера, мы смогли вернуть высоту контейнера к нормальному уровню.
В предыдущих примерах мы увидели, как задать обтекание картинки текстом так, чтобы картинка оказалась слева.Точно так же мы можем обтекать её и с другой стороны.
img { float: right; }
И если мы не уверены, что текста будет достаточно, не забудем добавить в конец контейнера clear: both.
img { float: left; } .clear { clear: both; }
<p> <img src="./assets/images/image.svg" alt="Just image"> Lorem ipsum dolor sit amet consectetur adipisicing elit. Hic commodi maiores excepturi dolore cupiditate sit obcaecati, repellat doloribus rerum! Dicta eius aliquid eaque sed sequi obcaecati debitis quos veniam placeat. </p>
Подведём итоги. Для того, чтобы вставить картинку в текст и сделать так, чтобы она обтекалась, нужно воспользоваться свойством
float, а для того, чтобы восстановить размер контейнера в том случае, если картинка стала вылезать за его пределы, нужно использовать свойство clear.CSS переменные
В крупных проектах часто используется ограниченная палитра цветов, составленная дизайнером. В ходе разработки, мы
используем эту палитру по всему проекту. Проект растёт, количество файлов в нём, соответственно, тоже. И вот,
наступает он — РЕДИЗАЙН. Слово, доставляющее боль и страдания любому верстальщику.
Так уж получилось, что вам повезло и в проекте просто поменялось несколько цветов. Допустим, салатовый стал
ярко-зелёным, а красный стал томатным.
Звучит не так уж и сложно. Простенький такой редизайн. И пошли мы по всем CSS-файлам проекта искать изменённые
цвета. Причём, через какое-то время
мы ещё и заметили, что в прошлый раз, дизайнер немножко ошибался с цветами и вместо одного кода салатового
цвета#3bff00
, он использовал множество вариаций, незначительно отличающихся друг от друга. К
примеру,#3bff01
. Как мы понимаем, простым поиском и заменой нам уже не обойтись. Теперь придётся искать
на всех страницах вашего сайта салатовые элементы.
Уже представляете, сколько времени у вас на это уйдёт? А ведь нам еще красный на томатный менять.
В борьбу с такими вот проблемами включились CSS переменные (CSS custom properties) (пользовательские свойства CSS) . Мы можем изначально задать переменную для салатового цвета и использовать только её. А если мы при разработке увидим, что есть два почти одинаковых салатовых цвета, то можем подойти к дизайнеру и спросить, какой из них правильный. Дизайнеры тоже люди и тоже ошибаются. Это надо учитывать. Итак, зададим два цвета: салатовый и томатный:
:root { --green-light: #3bff00; --tomato: #ff6347; }
Как вы видите, CSS переменные задаются в псевдо-классе:root
Теперь будем использовать эти переменные в CSS:
menu { background-color: var(--green-light); border: 1px solid var(--tomato); }
К следующему редизайну, когда#3bff00
поменяется на#00ad68
, мы лишь изменим код цвета
в:root
, и весь проект перекрасится. Одна секунда вместо часов страданий.
Рекомендую называть переменные не так, как в примере. Наиболее правильными будут названия, исходя из того, где
используется цвет или вообще какие-то абстрактные названия. Например--accent, --primary, --frame-shadow
,
...
Улучшение адаптивности
В CSS переменные можно задавать не только цвета, но и числа. А ещё мы можем изменять переменные, в зависимости от того,
каким устройством пользуется посетитель нашего сайта.:root { --font-large: 20px; } h3 { font-size: var(--font-large); } @media(min-width: 700px) { :root { --font-large: 32px; } }
Так, если ширина экрана пользователя больше 700 пикселей, все свойства, использующие переменную
--font-large
, увеличатся.
Кроме того, к CSS переменным можно получить доступ из JavaScript, но это уже совсем другая история
Выравнивание блока по центру
Этот фокус позволяет центрировать блок заранее определённой ширины, независимо от ширины экрана. Кроме того, если использовать max-width вместо width, то, даже если размер экрана пользователя окажется меньше ширины этого блока, то блок не выйдет за границы экрана, а будет равен ширине экрана. Это весьма распространенная практика
Пример:
Код:
<div class="block"></div>
.block { max-width: 500px; margin: 0 auto; /* центрирует блок так, что правый и левый маргин становятся одинаковыми */ }
.block { height: 110px; background: teal; }
Задача
Сделать шапку для сайта avito.ru. Серый фон шапки должен быть растянут на всю страницу, а элементы меню должны быть в центре, не зависимо от ширины экрана пользователя
<div class="sol1"> <div class="content"> <div class="left"><a href="#">Объявления</a><a href="#">Магазины</a><a href="#">Бизнес</a><a href="#">Помощь</a></div> <div class="right"><a class="primary" href="#">Вход и регистрация</a><a class="button primary" href="#">Подать объявление</a></div> </div> </div>
.sol1 { background-color: #f7f7f7; font-size: 14px; border-bottom: 1px solid #d8d8d8; border-top: 1px solid #d8d8d8; } .sol1 .content { margin: 0 auto; max-width: 900px; display: flex; justify-content: space-between; padding: 0 32px; } .sol1 .content .left { padding: 14px 0; } .sol1 .content .left a { margin-right: 18px; } .sol1 .content .right { padding: 7px 0; } .sol1 .content .right a.button { margin-left: 29px; } .sol1 a { color: #a1a1a1; text-decoration: none; } .sol1 a:hover { color: #ff6163; } .sol1 .button { display: inline-block; padding: 6px 13px; border-radius: 3px; border: 1px solid transparent; box-shadow: none; } .sol1 a.primary { color: #0091d9; } .sol1 a.primary:hover { color: #ff6163; } .sol1 .button.primary { background-color: #01aaff; color: white; } .sol1 .button.primary:hover { color: white; background-color: #0099e6; } @media (max-width: 720px) { .sol1 { font-size: 1.5vw; } .sol1 .content { padding: 0 3.5vw; } .sol1 .content .left { padding: 1.5vw 0; } .sol1 .content .left a { margin-right: 2vw; } .sol1 .content .right { padding: 0.7vw 0; } .sol1 .content .right a.button { margin-left: 3.2vw; padding: 0.6vw 1.5vw; } }
Самый правильный способ разнести блоки в разные стороны
Способ основывается на применении флекс-контейнера, внутри которого находятся два блока. Оперируя свойством justify-content, можно добиться различных вариантов размещения этих двух блоков внутри флекс-контейнера. Нас же сейчас интересует значение "space-between", равномерно распределяющее блоки по ширине флекс-контейнера, прижимая к его краям первый и последний блоки. Ширина блоков будет выбираться автоматически, в зависимости от контента, содержащегося внутри блока. Кроме того, ширину блока, при необходимости, можно зафиксировать свойством width, если это потребуется.
Этот метод используется, в основном, для построения "шапки" сайта, но может быть использован и в других частях
сайта или
Пример:
Код:
<div class="container"> <div class="box-left"> <span>Brand</span> <span>Stores</span> <span>Actions</span> <span>Contacts</span> </div> <div class="box-right"> <span>Settings</span> <span>Account</span> </div> </div>
{ /* Задаём флекс-бокс (позволяет влиять на расположение дочерних элементов) */ display: flex; /* Говорим элементам встать так, чтобы между ними, по возможности, образовывалось расстояние */ justify-content: space-between; } .box-left { /* Здесь можно задать какие-то стили для левого блока */ } .box-right { /* Здесь можно задать какие-то стили для правого блока */ }
.container { background: lightblue; padding: 5px; } .box-left { background: teal; } .box-right { background: blueviolet; } .container span { display: inline-block; padding: 10px 20px; background: rgba(255, 255, 255, 0.5); margin: 5px; } @media (max-width: 720px) { .container { padding: 5px 1px; font-size: 3.5vw; } .container span { margin: 2px; padding: 10px 2px; } }
Задача
Сделать шапку для сайта habr.com так, чтобы правая и левая часть меню были разнесены при помощи флекс-бокса
<div class="sol2"> <div class="content"> <div class="left"><a class="current" href="#">Публикации</a><a href="#">Новости</a><a href="#">Пользователи</a><a href="#">Хабы</a><a href="#">Компании</a><a href="#">Песочница</a></div> <div class="right"><a class="button default" href="#">Войти</a><a class="button primary" href="#">Регистрация</a></div> </div> </div>
.sol2 { border-bottom: 1px solid #d5dddf; border-top: 1px solid #d5dddf; font-size: 15px; background: white; margin-top:10px; } .sol2 .content { max-width: 900px; margin: 0 auto; padding: 0 32px; display: flex; justify-content: space-between; } .sol2 .right { padding: 16px; white-space: nowrap; } .sol2 .right .button { margin: 0 0 0 10px; } .sol2 a { display: inline-block; padding: 23px 0; margin-right: 28px; text-decoration: none; color: #838a92; } .sol2 a:hover { color: #5096b1; } .sol2 a.current { color: #464646; } .sol2 .button { padding: 8px 13px; border: 1px solid transparent; border-radius: 3px; font-size: 13px; box-shadow: none; } .sol2 .button.default { border-color: #a4afba; background-color: white; } .sol2 .button.default:hover { border-color: #65a3be; } .sol2 .button.primary { background-color: #65a3be; border-color: transparent; color: white; } .sol2 .button.primary:hover { background-color: #4986a1; } @media (max-width: 900px) { .sol2 { font-size: 1.5vw; } .sol2 .content { padding: 0 3.5vw; } .sol2 .right { padding: 1.5vw; } .sol2 .right .button { margin: 0 0 0 0.5vw; } .sol2 a { padding: 2.5vw 0; margin-right: 1.4vw; } .sol2 .button { padding: 0.8vw 1.4vw; font-size: 1.4vw; } }
Горизонтальное и вертикальное центрирование блока
Эта техника применяется, в основном, для вёрстки модальных окон. Основывается на том, что блокс position: absolute смещается относительно ближайшего родителя с position: relative | absolute | fixed. Смещение задаётся при помощи CSS-свойств
Если мы используем единицу измерения % для задания смещения, важно помнить, что процент берётся относительно родительского контейнера.Это значит, что left: 50% сместит блок на 50% от ширины родителя, а не от собственной ширины. Для того, чтобы сместить блок на 50% относительно собственной ширины, необходимо использовать свойство transform с функцией translateX(50%) - движение по оси X, translateY(50%) - движение по оси Y или translate(50%, 50%) - движение по обеим осям.
В примере ниже показано, как мы сначала смещаем абсолютно спозиционированный блок на 50% слева направо (left) и на 50% сверху вниз (top) относительно контейнера,а затем, смещаем на 50% вверх и на 50% вправо относительно самого себя (transform: translate(-50%, -50%)), чтобы блок встал ровно по центру контейнера.
Дело в том, что в CSS, началом координат является верхняя левая точка элемента, а не центр элемента.Поэтому нам не достаточно просто подвинуть блок на 50% вниз и вправо, так как тогда в центре контейнера окажется левый верхний угол блока, а не центр блока.
Пример:
Код:
<div class="container"> <div class="block"></div> </div>
.container { position: relative; width: 100%; height: 250px; } .block { position: absolute; /* позволяет двигать блок относительно родительского релятивного контейнера */ top: 50%; /* смещаем блок на 50% от высоты родителя */ left: 50%; /* смещаем блок на 50% от ширины родителя */ transform: translate(-50%, -50%); /* смещаем блок на 50% от ширины и высоты этого блока */ width: 120px; height: 80px; }
.container { background: lightblue; } .block { background: teal; }
Задача
Сделать просмотр изображений как на dns-shop.ru
- Размер внешнего контейнера: 100% * 100vh
- Вместо картинок можно использовать цветные блоки
- Стрелочки: arrow-left, arrow-right; изображение кнопки "закрыть": close.
<div class="sol3"> <div class="title">iPhone Xs Max</div> <button class="button close"></button> <button class="button left"></button> <button class="button right"></button> <div class="image-container"><img src="./assets/images/iphonexsmax-1.jpg" alt="iPhone Xs Max"></div> <div class="slider-container"> <button class="button left"></button> <button class="thumb active"><img src="./assets/images/iphonexsmax-2.jpg" alt="Next"></button> <button class="thumb"><img src="./assets/images/iphonexsmax-3.jpg" alt="Next"></button> <button class="thumb"><img src="./assets/images/iphonexsmax-4.jpg" alt="Next"></button> <button class="thumb"><img src="./assets/images/iphonexsmax-5.jpg" alt="Next"></button> <button class="button right"></button> </div> </div>
.sol3 { position: relative; width: 100%; height: 100%; min-height: 550px; overflow: hidden; background: white; } .sol3 .title { position: absolute; top: 30px; left: 30px; font-size: 24px; font-weight: bold; color: #444; } .sol3 .image-container { position: absolute; bottom: 50%; right: 50%; transform: translate(50%, 50%); width: 70%; height: 60%; text-align: center; } .sol3 .image-container img { max-width: 100%; max-height: 100%; object-fit: contain; } .sol3 .slider-container { position: absolute; bottom: 30px; right: 50%; width: 320px; height: 80px; margin-right: -160px; display: flex; justify-content: space-between; } .sol3 .slider-container .thumb { text-align: center; padding: 8px; background: white; border: 1px solid #d8d8d8; margin: 5px; width: 70px; height: 70px; border-radius: 4px; cursor: pointer; box-shadow: none; } .sol3 .slider-container .thumb img { display: inline-block; max-width: 100%; max-height: 100%; opacity: 0.7; } .sol3 .slider-container .thumb.active { border-color: #464646; } .sol3 .slider-container .thumb.active img, .sol3 .slider-container .thumb:hover img { opacity: 1; } .sol3 .slider-container .button { width: 60px; } .sol3 .slider-container .button.left { left: auto; right: 100%; width: 40px; } .sol3 .slider-container .button.right { right: auto; left: 100%; width: 40px; } .sol3 .button { position: absolute; bottom: 50%; transform: translateY(50%); background: white; min-width: 30px; min-height: 30px; border: none; cursor: pointer; opacity: 0.4; background-repeat: no-repeat; background-position: center center; background-size: contain; width: 80px; height: 80px; box-shadow: none; } .sol3 .button:hover { opacity: 1; } .sol3 .button.left { left: 0; background-image: url('./assets/images/angle-left-sm.svg'); } .sol3 .button.right { right: 0; background-image: url('./assets/images/angle-right-sm.svg'); } .sol3 .button.close { position: absolute; top: 30px; right: 30px; transform: none; width: 30px; height: 30px; background-image: url('./assets/images/times-sm.svg'); } @media (max-width: 500px) { .sol3 .slider-container { transform: scale(0.8); } }
Многоколоночные макеты
При вёрстке сайта, одной из первых задач верстальщика является создание основного шаблона.Основным шаблоном в данном случае называется вёрстка без контента - это совокупность шапки сайта, меню, футера и прочих элементов, которые встречаются на каждой странице.
Итак, попробуем сверстать простой макет сайта. Сверху у нас будет шапка сайта, слева будет навигация по разделу сайта, справа контент, а снизу футер.Стандартный шаблон, ничего необычного. Начнём с HTML.
<div class="main-container"> <header>Header</header> <nav>Site navigation</nav> <main>Site content</main> <footer>Footer</footer> </div>
Если мы запустим это прямо сейчас, мы лишь увидим, как все эти элементы расположились друг под другом. Придётся добавить стилей.
Float
В древние времена существовало несколько вариантов вёрстки макетов, а самым адекватным из них был float. Как мы выяснили в одной из предыдущих глав,с помощью float можно сделать так, чтобы картинка обтекалась текстом. То же можно применить и к блокам. Если мы возьмём два блочных элемента
с определенными заранее шириной и высотой, то, добавив к нимfloat: left;
, мы увидим, как эти два блочных элемента выстроятся в один ряд, если ширина страницы это позволит.
.main-container { margin: 0 auto; max-width: 800px; } header { height: 50px; background: cornflowerblue; } nav { height: 80px; background: darksalmon; width: 20%; /* Задаём ширину блока навигации */ float: left; /* Устанавливаем обтекание */ } main { height: 120px; background: khaki; width: 80%; /* Задаём ширину контента */ float: left; /* Устанавливаем обтекание */ } footer { clear: both; /* Сбрасываем обтекание */ height: 50px; background: darkseagreen; }
Казалось бы, на этом можно закончить наш урок, но, если хорошо подумать, можно увидеть, как много проблем мы породили этим решением:
- Мы не можем жёстко зафиксировать ширину навигационного меню
- Если перед футером нам потребуется вставить какой-то блок, например, рекламный, вёрстка "поедет", ведь мы благополучно забудем про
clear: both;
- Высота навигации никак не зависит от высоты контента и наоборот. Под более коротким блоком будет пустота.
Подробнее о float читайте тут: float
Flex
Рассмотрим более современный способ. Мы можем использоватьdisplay: flex;
у.main-container
, тогда мы сможем избавиться от пары недостатков предыдущего примера.
.main-container { margin: 0 auto; max-width: 800px; display: flex; /* Объявляем контейнер флексом */ align-items: stretch; /* Высота элементов должна подстраиваться по высоте самого высокого элемента */ flex-wrap: wrap; /* Если не хватает места в строке, переносить блок на следующую */ } header { width: 100%; /* Обязательно задаём ширину для шапки */ height: 50px; background: cornflowerblue; } nav { background: darksalmon; width: 20%; /* Задаём ширину навигационного меню */ } main { height: 120px; background: khaki; width: 80%; /* Задаём ширину контента */ } footer { width: 100%; /* Обязательно задаём ширину для футера */ height: 50px; background: darkseagreen; }
Как мы видим, каким бы длинным ни был контент, под навигацией не образуется пробела, а так как больше нет флоатов, то перед футером можно вставить любой блок.К сожалению, таким способом мы не смогли решить последнюю проблему: как задать у меню фиксированную ширину, а ширина контента при этом была переменной?
Больше о flex здесь:
Grid
Самым современным способом вёрстки макетов на сегодняшний день является т.н. Grid layout. Грид позволяет выстраивать элементы внутри себя по заранее заданнойсетке. Для начала разберём пример, а затем разберёмся подробнее, как это работает.
.main-container { margin: 0 auto; max-width: 800px; display: grid; /* Объявляем контейнер гридом */ grid-template-rows: 50px 1fr 50px; /* (1) Задаём строки */ grid-template-columns: 200px 1fr; /* (2) Задаём колонки */ grid-template-areas: 'header header' /* (3) Задаём названия областей */ 'nav content' 'footer footer'; } header { grid-area: header; /* Шапка займёт область с названием header */ background: cornflowerblue; } nav { background: darksalmon; grid-area: nav; /* Навигация займет область nav */ } main { height: 120px; background: khaki; grid-area: content; /* Контент займёт content */ } footer { grid-area: footer; /* Футер - footer */ background: darkseagreen; }
Как можно заметить, мы избавились от всех негативных последствий. А теперь разберёмся подробнее, как это работает.
grid-template-rows: 50px 1fr 50px;
означает, что если смотреть на сайт по вертикали, то у нас образуются три строки: шапка, навигация с контентом, футер. Высоту первой строки, шапки, установим равной 50px, высота второй строки нам неизвестна, а высота третьей, футера, тоже 50px.grid-template-columns: 200px 1fr;
означает, что если смотреть на сайт по горизонтали, то образуются два столбца: слева навигация, справа контент. Ширину навигации установим равной 200px, а ширина контента нам неизвестна и она займёт всё доступное пространство.- Итак, на этом этапе мы поняли, что у нас будет три строки и два столбца. Это и указываем далее:
grid-template-areas: 'header header' 'nav content' 'footer footer';
. Этот код создаёт три строки и два столбца, состоящие из именованных областей. Именовать эти области можно как угодно, их названия будут указаны в дочерних элементах в свойствеgrid-area
. Здесь мы видим, что шапка займёт две колонки; навигация — одну, левую колонку; контент — одну правую; футер — две колонки.
Таким образом, мы получили идеальную сетку для нашего сайта. Подробнее о grid layout читать здесь:
Calc
А теперь настало время удивляться. Вернёмся к примеру с флексом и добавим еще немного CSS:
nav { width: 200px; } main { width: calc(100% - 200px); }
Таким образом мы избавились от проблемы фиксированной ширины меню. Благодаря функцииcalc()
, мы заставили ширину контента зависеть от ширины блока навигации.
О calc() можно почитать здесь
Ещё один вариант вёрстки шаблона
На самом деле, есть еще один вариант. Он сложный и ненадёжный, но для общего развития, стоит упомянуть и его.
.main-container { margin: 0 auto; max-width: 800px; position: relative; padding: 50px 0 50px 200px; box-sizing: border-box; } header { position: absolute; top: 0; right: 0; left: 0; height: 50px; background: cornflowerblue; } nav { background: darksalmon; position: absolute; top: 50px; bottom: 50px; left: 0; width: 200px; overflow-y: auto; } main { height: 120px; background: khaki; width: 100%; } footer { position: absolute; bottom: 0; right: 0; left: 0; height: 50px; background: darkseagreen; }
Что тут сделано?
- Основной контейнер спозиционирован относительно, чтобы абсолютно-спозиционированные элементы выстраивались внутри него.
- Шапка и футер прижимаются к верхней и нижней части контейнера.
- Навигация прижимается к левой части контейнера.
- Чтобы блоки не налезали друг на друга, с помощью внутренних отступов у основного контейнера, отделим контент от остальных блоков.
- Так как теперь высота блока навигации зависит от высоты контента, добавляем свойство
overflow
, чтобы при недостаточной высоте навигации, внутри неё появлялся скроллбар.
Сетки
Сетки нужны для того, чтобы правильно расположить содержимое на странице. Обычно, системы сеток состоят из контейнеров (container), рядов (row) и колонок (col). Распространённой практикой являются 12-колоночные сетки. Наиболее известная система сеток среди верстальщиков - это Bootstrap Grid layout
В этой главе мы попробуем самостоятельно создать простую систему сеток.
Float
Когда-то давно существовал лишь один более или менее хороший подход к проектированию сеток — сетки на флоатах.
<div class="container"> <div class="row"> <div class="col w8"> <span>Ячейка w8</span> </div> <div class="col w4"> <span>Ячейка w4</span> </div> </div> <div class="row"> <div class="col w4"> <span>Ячейка w4</span> </div> <div class="col w4"> <span>Ячейка w4</span> </div> <div class="col w4"> <span>Ячейка w4</span> </div> </div> <div class="row"> <div class="col w3"> <span>Ячейка w3</span> </div> <div class="col w3"> <span>Ячейка <br> w3</span> </div> <div class="col w3"> <span>Ячейка w3</span> </div> <div class="col w3"> <span>Ячейка w3</span> </div> </div> <div class="row"> <div class="col w6"> <span>Ячейка w6</span> </div> <div class="col w6"> <span>Ячейка w6</span> </div> </div> </div>
* { box-sizing: border-box; } { max-width: 800px; margin: 0 auto; padding: 0 5px; } .row { margin: 0 -5px 10px; } .row::after { content: ''; display: block; clear: both; } .col { width: calc(100% / 12); float: left; padding: 0 5px; } .col.w1 { width: calc(100% / 12); } .col.w2 { width: calc(100% * 2 / 12); } .col.w3 { width: 25%; } .col.w4 { width: calc(100% * 4 / 12); } .col.w5 { width: calc(100% * 5 / 12); } .col.w6 { width: 50%; } .col.w7 { width: calc(100% * 7 / 12); } .col.w8 { width: calc(100% * 8 / 12); } .col.w9 { width: 75%; } .col.w10 { width: calc(100% * 10 / 12); } .col.w11 { width: calc(100% * 11 / 12); } .col.w12 { width: 100%; }
.col span{ display: block; background: lightblue; padding: 10px; height: 100%; } .col:nth-child(n+1) span{background: cornflowerblue} .col:nth-child(n+2) span{background: darkseagreen} .col:nth-child(n+3) span{background: khaki} .col:nth-child(n+4) span{background: darksalmon}
Как видим, всё не так уж и плохо. Лишь пара неприятных моментов: высоты колонок никак не зависят друг от друга, приходится подстраивать паддинги и маргины, чтобы между колонками было расстояние, а ещё приходится писать много кода.
Flex
Попробуем исправить предыдущий пример.
<div class="container"> <div class="row"> <div class="col w8"> <span>Ячейка w8</span> </div> <div class="col w4"> <span>Ячейка w4</span> </div> </div> <div class="row"> <div class="col"> <span>Ячейка</span> </div> <div class="col"> <span>Ячейка</span> </div> <div class="col"> <span>Ячейка</span> </div> </div> <div class="row"> <div class="col"> <span>Ячейка</span> </div> <div class="col"> <span>Ячейка <br> вторая строка</span> </div> <div class="col"> <span>Ячейка</span> </div> <div class="col"> <span>Ячейка</span> </div> </div> <div class="row"> <div class="col"> <span>Ячейка</span> </div> <div class="col"> <span>Ячейка</span> </div> </div> </div>
* { box-sizing: border-box; } { max-width: 800px; margin: 0 auto; padding: 0 5px; } .row { display: flex; flex-wrap: wrap; margin: 0 -5px 10px; } .col { width: auto; max-width: 100%; flex-shrink: 0; flex-grow: 1; flex-basis: 0; padding: 0 5px; } .col.w1 { min-width: calc(100% / 12); flex-basis: calc(100% / 12)} .col.w2 { min-width: calc(100% * 2 / 12); flex-basis:calc(100% * 2 / 12); } .col.w3 { min-width: 25%; flex-basis: 25%; } .col.w4 { min-width: calc(100% * 4 / 12); flex-basis: calc(100% * 4 / 12); } .col.w5 { min-width: calc(100% * 5 / 12); flex-basis: calc(100% * 5 / 12); } .col.w6 { min-width: 50%; flex-basis: 50%; } .col.w7 { min-width: calc(100% * 7 / 12); flex-basis: calc(100% * 7 / 12); } .col.w8 { min-width: calc(100% * 8 / 12); flex-basis: calc(100% * 8 / 12); } .col.w9 { min-width: 75%; flex-basis: 75%; } .col.w10 { min-width: calc(100% * 10 / 12); flex-basis: calc(100% * 10 / 12); } .col.w11 { min-width: calc(100% * 11 / 12); flex-basis: calc(100% * 11 / 12); } .col.w12 { min-width: 100%; flex-basis: 100%; }
Как видите, у этого метода есть свои плюсы. Когда нам нужны одинаковые колонки в ряду, нам не нужно указывать ширину этих колонок. И, хоть мы и избавились от хака с clear: both, кода меньше не стало. И снова подстраивать расстояния между блоками приходится не без фокусов, хотя и высоты блоков теперь можно сделать зависимыми друг от друга. Однако, этот способ действительно гораздо лучше предыдущего.
Grid
Уже из названия этого способа понятно, что он придуман как раз для решения этой проблемы.
Для начала, избавимся от строк. Они нам больше не нужны.
<div class="container"> <div class="grid g-2-1"> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> </div> <div class="grid g-1-1-1"> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> </div> <div class="grid g-1-1-1-1"> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка <br> вторая строка </div> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> </div> <div class="grid g-1-1"> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> </div> </div>
.container { max-width: 800px; margin: 0 auto; } .cell { padding: 0 10px; } .grid { display: grid; gap: 10px; margin-bottom: 10px; } .grid.g-2-1 { grid-template-columns: 2fr 1fr; } .grid.g-1-1 { grid-template-columns: 1fr 1fr; } .grid.g-1-1-1 { grid-template-columns: repeat(3, 1fr); } .grid.g-1-1-1-1 { grid-template-columns: repeat(4, 1fr); }
.cell { background: lightblue; padding-top: 10px !important; padding-bottom: 10px !important; } .cell:nth-child(4n+1){background: cornflowerblue} .cell:nth-child(4n+2){background: darkseagreen} .cell:nth-child(4n+3){background: khaki} .cell:nth-child(4n+4){background: darksalmon}
Это совершенно иной подход к организации сеток. Как видите, здесь уже нет проблем с расстояниями между колонками, благодаря свойству gap, кроме того, если вам нужна ровная сетка, кода станет гораздо меньше:
<div class="container"> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка <br> вторая строка </div> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> <div class="cell"> Ячейка </div> </div>
.container { max-width: 800px; margin: 0 auto; display: grid; gap: 10px; grid-template-columns: repeat(3, 1fr); } .cell { padding: 0 10px; }
Теперь нам не обязательно иметь строки, а сам грид может быть совмещён с контейнером.
Поэкспериментировать с этим примером можно здесь:
Запустить примерКроме того, с помощью CSS Grid можно создавать вот такие необычные сетки:
Запустить примерОтзывчивый дизайн и медиа-выражения
Практически любой сайт (или веб-приложение) проектируется как для компьютера, так и для мобильного телефона. На сегодняшний день мобильный трафик несколько обходит трафик персональных компьютеров, что означает, что нашей задачей, как разработчиков интерфейсов, является адаптация интерфейса как под экраны компьютеров, так и под экраны телевизоров и мобильных телефонов.
CSS содержит специальные правила, которые могут включаться в зависимости от разрешения экрана пользователя. Здесь мы рассмотрим лишь базовые примеры, но настоятельно рекомендую углубиться в эту тему.
Существует два подхода к вёрстке: desktop-first и mobile-first. Когда мы говорим о desktop-first, это означает, что сайт изначально проектируется под большой экран, а далее видоизменяется к меньшим разрешениям, подключая дополнительные правила. При использовании подхода mobile-first, сайт проектируется под экраны мобильных телефонов, а затем, если разрешение экрана позволяет, подключает дополнительные правила расположения блоков на странице, чтобы сайт перестраивался под большие экраны. Общепринятым стандартом является использование подхода mobile-first. Дело в том, что мобильные устройства считаются более слабыми, поэтому мы можем сэкономить их вычислительный ресурс, не давая включать правила для больших экранов. Пойдём от обратного. Представим, что мы используем подход desktop-first. Тогда, при открытии сайта с телефона, браузер сначала рассчитает все правила для больших экранов, затем поймет, что разрешение экрана маленькое и включит еще слой дополнительных правил. Именно в этом месте у нас и произойдет экономия. Пусть устройства с большими экранами подключают дополнительные правила, ведь почти всегда эти устройства имеют большую вычислительную мощность.
Для задания правила используется ключевое слово @media, где указывается, например, минимальная (для подхода mobile-first) ширина экрана.
Рассмотрим пример:
@media(min-width: 720px) { ... }
Здесь мы видим, что если разрешение экрана больше 720 точек, мы включим дополнительные CSS-правила, позволяющие иначе расположить блоки на странице. Внутри фигурных скобок мы можем писать обычный CSS-код.
Разберём задачу. Пусть нам необходимо расположить 6 прямоугольных блоков на странице таким образом, чтобы на экране мобильного телефона они располагались друг под другом, на экране планшета мы видели по два блока на строку, а на экране компьютера — по три блока на строку.
Используем подход mobile-first. Опишем изначальный HTML:
<ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> </ul>
И CSS:
ul { display: block; list-style: none; margin: 0; padding: 0; } li { display: block; width: 100%; float: left; }
Теперь сделаем так, чтобы на планшетах в одной строке было два блока. Сделаем предположение, что планшеты - это устройства с экранами более 720 точек в ширину.
@media(min-width: 720px) { li { width: 50%; } }
И сделаем ещё одно правило для крупных устройств:
@media(min-width: 1200px) { li { width: 33%; } }
Таким образом мы реализовали отзывчивый список, который подстраивается под любое разрешение экранов. Проверить его работы можно ниже. Изменяйте размер примера с помощью ресайзера внизу справа или изменяйте размер окна браузера, чтобы увидеть пример в действии.
Запустить примерГрадиенты, тени и фильтры
Почти на каждом макете, который вам даст дизайнер, вы увидите какие-то плавные переходы цветов, элементы, будто парящие над другими элементами, цветовые эффекты у изображений, происходящие с ними, например, при наведении курсора мыши. В этой главе мы разберём часть из этих эффектов.
Градиенты
Градиенты — это плавные перетекания одних цветов в другие, используемые, в основном, для фона. В CSS градиенты бывают двух типов: линейные и радиальные.
Линейные градиенты задаются функцией linear-gradient()
, которая принимает в качестве параметров
направление градиента (в радианах - deg) и список точек (минимум 2), содержащие цвет и позицию точки (в
процентах).
<div class="gradient"></div>
.gradient { background: linear-gradient( 90deg, /* задаём направление градиента */ blueviolet 0%, /* задаём цвет и позицию первой точки градиента */ tomato 100% /* задаём цвет и позицию второй точки градиента */ ); height: 200px; }
Подробнее о линейном градиенте можно прочитать здесь: MDN: linear-gradient()
Радиальные градиенты задаются функциейradial-gradient()
, которая принимает в качестве параметров
форму градиента (circle или ellipse) и список точек, содержащие цвет и позицию точки.
<div class="gradient"></div>
.gradient { background: radial-gradient( circle, /* задаём форму градиента */ blueviolet 0%, /* задаём цвет и позицию первой точки градиента */ tomato 100% /* задаём цвет и позицию второй точки градиента */ ); height: 200px; }
Подробнее о радиальном градиенте можно прочитать здесь: MDN: radial-gradient()
Для быстрого формирования градиентов существуют генераторы градиентов, один из которых я рекомендую своим читателям: CSS Gradient
Тени
Тени в CSS подразделяются на два вида: тени текста и тени блоков.
Тени текста задаются свойством text-shadow
, принимающим четыре параметра: сдвиг тени по оси X, сдвиг
тени по оси Y, размытие тени и её цвет.
Тени блоков задаются свойством box-shadow
, принимающим пять параметров: сдвиг по оси X, сдвиг по оси
Y, размытие тени, радиус распространения и её цвет.
Рассмотрим пример:
<div class="shadow">Текст с тенью внутри блока с тенью</div>
.shadow { text-shadow: 1px 3px 5px grey; box-shadow: 0 5px 10px -3px black; max-width: 320px; padding: 50px; font-size: 18px; text-align: center; border-radius: 10px; color: teal; background: white; margin: 20px auto; }
Разберёмся подробнее с параметрами теней. Сдвиги по осям X и Y позволяют перемещать тень относительно текста. Они могут быть как положительными, так и отрицательными,
причём отрицательные сдвиги будут перемещать тень влево и вверх, соответственно. Размытие тени может быть только положительным числом. Благодаря этому параметру можно создать эффект нечёткой тени, положительно влияющий на реалистичность. Радиус распространения тени может быть как положительным, так и отрицательным числом. Это свойство применимо только кbox-shadow
. По умолчанию оно равно 0, что означает, что площадь тени
будет равна площади блока. Положительные значения увеличат площадь тени относительно блока, а отрицательные —
уменьшат, что позволит создать эффект парящего блока.Поиграть с box-shadow можно здесь: Box-Shadow CSS Generator, а с text-shadow тут: Text Shadow CSS Generator.
Фильтры
Фильтры в CSS решают примерно ту же задачу, что и фильтры в фотошопе или инстаграме (кому что ближе). Обычно, фильтры применяются к изображениям, но они могут быть применены
к любому элементу на странице. Существует несколько основных фильтров, но можно применять и собственные SVG-фильтры, если вы достаточно уверенно себя чувствуете, работая с этой технологией.blur()
— применяет к элементу размытие по Гауссу (px)brightness()
— влияет на яркость изображения. (0% - 100%)contrast()
— изменяет контрастность изображения. (0% - 100%)drop-shadow()
— ведёт себя похожим образом наbox-shadow()
, но, применяясь к изображению с прозрачным фоном или сложной геометрической фигуре, образует тень, обтекающую именно фигуру, а не блок. Пример:filter: drop-shadow(1px 1px 5px 0 black);
grayscale()
— делает изображение чёрно-белым. (0% - 100%)hue-rotate()
— вращает цвета изображения по HSL-кругу. (0deg - 360deg)invert()
— создаёт негатив изображения. (0% - 100%)opacity()
— ведёт себя так же, как и обычное свойство opacity, изменяя прозрачность изображения. (0% - 100%)saturate()
— устанавливает насыщенность для изображения. (0% - 100%)sepia()
— имитирует эффект "старинного снимка". (0% - 100%)url()
— принимает ссылку на SVG-фильтр и применяет этот фильтр к изображению.
Этот пример от html5book наглядно демонстрирует возможности фильтров: CSS3 Filters
Трансформации
Существует несколько базовых трансформаций:
rotate(45deg)
позволяет вращать элемент.scale(2)
масштабирует элемент. Может принимать два параметра, позволяя растягивать элемент или даже отражать его.skew(10deg, 10deg)
наклоняет элемент в одной или двух плоскостях.translate(10px, 20px)
сдвигает элемент по одной или двум осям.matrix(...)
трансформирует элемент согласно матрице преобразований
Есть и другие виды трансформаций. О них рекомендую почитать здесь: MDN: CSS Transforms
Вы можете комбинировать несколько трансформаций. К примеру, можно одновременно сдвинуть и повернуть элемент. ВАЖНО: порядок трансформаций играет роль.
.figure { font-size: 12px; width: 50px; height: 50px; background: var(--accent); color: var(--white-dark); display: flex; align-items: center; justify-content: center; position: absolute; left: 100px; top: 20px; } .border { border: 1px dashed var(--grey); background: transparent; }
<div class="figure border"></div> <div class="figure transform">ABCD</div>
.transform { transform: translate(0px, 100px) rotate(45deg); }
.transform { transform: rotate(45deg) translate(0px, 100px); }
Обратите внимание, что в первом случае фигура была сначала сдвинута на 100px вниз, а затем повёрнута на 45 градусов, а во втором случае — сначала повёрнута на 45 градусов, а затем сдвинута вниз (относительно своего текущего положения в пространстве; то есть "низом" считается низ самой фигуры).
Вёрстка тултипов
HTML имеет свои встроенные тултипы, но часто их дизайн не соответствует требованиям стиля макета. Наведите мышь на следующее предложение, чтобы понять,что такое тултип и как он выглядит по умолчанию. Это предложение имеет тултип. Сейчас мы научимся стилизовать тултипы. Для начала, нужно понять, что стандартный тултип - это своеобразное описание для элемента.
Самый простой вид тултипа, который мы здесь будем рассматривать, не требует от HTML практически ничего. Мы будем использовать атрибут aria-label, который служит для описания элементов скринридерам (в основном, для людей с нарушениями зрения).
Для того, чтобы через CSS "захватить" значение атрибута, необходимо использовать свойство content, использующееся с псевдо-элементами ::before и ::after.
Итак, рассмотрим код:
.tooltip { position: relative; /* Элемент должен быть спозиционирован не статично, чтобы внутри него можно было разместить абсолютно-спозиционированный элемент */ } .tooltip:hover::after { content: attr(aria-label); /* Захватываем атрибут aria-label и отображаем его в ::after псевдо-элементе */ position: absolute; /* Позиционируем псевдо-элемент абсолютно */ bottom: 100%; left: 0; background: rgba(0,0,0,.5); color: white; border-radius: 4px; padding: 8px 16px; font-size: 12px; cursor: default; } .like-button { /* Какие-то стили для кнопки */ }
.like-button { position: relative; background: none; border: none; padding: 0; font-size: 24px; color: tomato; margin: 40px 0; cursor: pointer; } .like-button:hover { color: darkred; } a { color: white; font-size: inherit; } a:hover { text-decoration: none; } @media print { .like-button.tooltip::after { content: attr(aria-label); position: absolute; bottom: 100%; left: 0; background: rgba(0,0,0,.5); color: white; border-radius: 4px; padding: 8px 16px; font-size: 12px; cursor: default; } .like-button .tooltip { display: inline-block !important; } }
<button class="like-button tooltip" aria-label="Like">❤</button>
Наведи на сердечко, чтобы увидеть тултип
Тултип в виде HTML-элемента
К сожалению, рассмотренный выше тултип не идеален. Что делать, если внутри него нужно разместить ссылку? А многострочный текст? Да, бывают и такие задачи.Чтобы решить эту задачу, нам придётся использовать тултип в виде HTML-элемента.
.has-tooltip { position: relative; } .has-tooltip:hover .tooltip { display: inline-block; /* Отображаем тултип только если навели мышью на элемент, его содержащий */ } .tooltip { display: none; /* Скрываем тултип по умолчанию */ position: absolute; bottom: 100%; left: 0; min-width: 350px; text-align: left; background: rgba(0,0,0,.5); color: white; border-radius: 4px; padding: 8px 16px; font-size: 12px; margin-bottom: 8px; cursor: default; } .tooltip::after { /* Это уголок */ content: ''; position: absolute; top: 100%; left: 4px; border: 8px solid transparent; border-top-color: rgba(0,0,0,.5); }
<button class="like-button has-tooltip" aria-label="Like"> ❤ <span class="tooltip"> А здесь мы уже можем размещать различную информацию, <br> в том числе многострочный текст и даже <a>ссылки</a> </span> </button>
В примере показано не совсем корректное скрытие тултипа. Рекомендую использовать .visually-hidden.
Существует еще множество способов сделать тултип, но многие из них подразумевают использование JavaScript, а эта книга не о нём. На мой взгляд, то, что можно сделать без JavaScript, нужно делать без JavaScript. Конечно, и из этого правила есть исключения.
Выпадающее меню и дропдауны
Пользуясь интернетом, вы наверняка уже видели выпадающее меню. Это когда наводишь мышью на элемент меню и появляется подменю.
Весь секрет в том, чтобы изначально скрыть выпадающее меню, а при наступлении какого-то события (наведение мыши на какую-то кнопку), отобразить выпадающее меню.
Сразу же перейдём к вёрстке. HTML предельно прост: есть меню, состоящее из четырёх элементов, а во втором элементе, помимо ссылки, есть вложенное меню.
<ul> <li> <a>Магазины</a> </li> <li> <a>Покупателям</a> <ul class="dropdown"> <li> <a>Бонусная программа</a> </li> <li> <a>Подарочные карты</a> </li> <li> <a>Доставка</a> </li> </ul> </li> <li> <a>Юридическим лицам</a> </li> <li> <a>Контакты</a> </li> </ul>
CSS тоже не слишком сложный. Фактически, все стили вертятся вокруг оформления. Единственное - применен "магический"
селектор. На самом деле, никакой магии здесь нет. menu li:hover .dropdown
означает, что при наведении
курсора мыши :hover
, на элемент меню menu li
, дропдаун .dropdown
получит некоторые стили, в данном случае, display: block
.
* { box-sizing: border-box; } ul { list-style: none; padding: 0; margin: 0 auto 100px; max-width: 800px; display: flex; } ul li { display: block; position: relative; width: 25%; } ul li a { padding: 8px 16px; background: lightblue; display: inline-block; width: 100%; text-decoration: none; color: black; text-align: center; } ul li a:hover { background: teal; color: white; } ul li:hover .dropdown { /* Магия здесь */ display: block; } .dropdown { display: none; position: absolute; top: 100%; left: 0; padding: 0; margin: 0; min-width: 100%; list-style: none; } .dropdown li { display: block; width: auto; } .dropdown li a { text-align: left; }
a { cursor: pointer !important; } a:hover { text-decoration: underline; } @media print { .dropdown { display: block !important; } ul { margin-bottom: 250px; } }
Результат представлен ниже. Попробуйте навести мышью на элементы меню.
Похожим способом можно сделать и дропдаун для кнопки
<button>О нас <ul class="dropdown"> <li><a>О компании прессе</a></li> <li><a>Юридическая информация</a></li> <li><a>Контактная информация</a></li> </ul> </button>
button { background: white; border: 1px solid gray; border-radius: 4px; padding: 8px 16px; position: relative; margin-bottom: 100px; } button:hover { background: lightblue; } *:hover > .dropdown { /* при наведении на любой элемент, содержащий .dropdown ... */ display: block; } .dropdown { display: none; position: absolute; top: 100%; left: 0; padding: 16px; margin: 0; list-style: none; box-shadow: 0 0 5px 0 black; border-radius: 4px; background: white; min-width: 200px; } .dropdown li { display: block; margin-bottom: 8px; text-align: left; }
Вёрстка модальных окон
Практически каждый сайт использует модальные окна для различных целей: отображение уведомлений, подтверждение действий, сбор информации и прочее. В этой главе мы рассмотрим два способа отображения модальных окон: новый и универсальный.
Новые модальные окна
Новые модальные окна имеют очень простой синтаксис HTML:
<dialog open>Это модальное окно</dialog>
Для отображения модального окна, необходимо добавить атрибутopen
К сожалению, обойтись совсем без JavaScript здесь невозможно, поэтому далее я приведу пример кода, который минимизирует ваше взаимодействие с JavaScript.Мы можем использовать DATA-атрибуты для задания идентификатора модального окна, которое будет перехватываться JavaScript. Работать с этим просто:
<dialog data-modal="my-first-modal"> <button class="close">×</button> </dialog> <button data-modal-open="my-first-modal">Показать модальное окно</button>
Здесь, в атрибутеdata-modal
, мы задаем идентификатор модального окна. А далее, в кнопке, позволяющей
показать модальное окно, используя атрибутdata-modal-open
, мы говорим, что эта кнопка показывает
модальное окно с нашим идентификатором.
Простое решение может выглядеть примерно так:
Запустить примерСтилизация
Модальное окно, открытое с помощьюdialog.openModal()
, имеет псевдо-элемент::backdrop
.
Этот элемент является подложкой под модальным окном и растянут на весь экран. Обычно этот элемент красят в
прозрачно-чёрный цвет и добавляют к нему
backdrop-filter.
Этот набор фильтров позволяет, например, размыть всю страницу кроме модального окна для создания акцента на окно.
Кроме того, можно добавить эффект появления модального окна. Для этого можно добавить немного CSS-кода:
dialog { animation: fadeIn 250ms ease-in-out; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
Этот код позволит модальному окну плавно появляться на экране.
Рассмотренный выше способ в 2019 году работает далеко не во всех браузерах. Поддержку этого способа браузерами можно посмотреть здесь: Can i use Dialog element?
Универсальные модальные окна
Что делать, если нужно поддерживать большее количество браузеров? Выход один - использовать старые методы. Код будет не так изящен, зато работать будет везде.
<button data-modal-open="hello-world">Открыть модальное окно</button> <div class="modal" data-modal="hello-world"> <div class="dialog"> <button class="dialog-close" aria-label="Close modal"></button> <section class="dialog-content"> <h2>Модальное окно</h2> <p>Какой-то контент</p> </section> </div> </div>
Конечно же, придётся добавить много стилей:
.modal { /* Подложка будет занимать 100% площади экрана*/ position: fixed; top: 0; left: 0; bottom: 0; right: 0; width: 100%; height: 100vh; z-index: 1000; /* Скрываем подложку с модальным окном*/ display: none; /* Overflow позволит нам скроллить модальное окно, если контента в нём будет больше, чем может поместиться на экране */ overflow-y: auto; /* Цвет подложки */ background: rgba(0, 0, 0, 0.8); /* Размытие фона */ -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px); /* Анимация появления */ animation: fadeIn 250ms ease-in-out; /* Добавляем дополнительные стили */ align-items: flex-start; justify-content: center; padding: 5vh 0 10vh; /* Через JavaScript мы будем добавлять атрибут open, что должно сделать модальное окно видимым */ } .modal[open] { display: flex; } .modal .dialog { /* Стилизуем само модальное окно */ width: 100%; max-width: 960px; min-height: 100px; border-radius: 4px; position: relative; border: none; color: white; font-weight: 300; } .modal .dialog .dialog-close { /* Стилизуем кнопку закрытия модального окна */ position: absolute; top: 0; right: 0; width: 3em; height: 3em; background: none; border: none; cursor: pointer; outline: none; } .modal .dialog .dialog-close::before, .modal .dialog .dialog-close::after { content: ""; display: block; position: absolute; top: 50%; right: 5px; left: 5px; border-bottom: 1px solid white; transform: rotate(45deg); } .modal .dialog .dialog-close::after { transform: rotate(-45deg); } .modal .dialog .dialog-close:focus::before, .modal .dialog .dialog-close:hover::before, .modal .dialog .dialog-close:focus::after, .modal .dialog .dialog-close:hover::after { border-color: tomato; } .modal .dialog .dialog-content { /* Дополнительные стили для контента*/ padding: 2em; background-color: rgba(0, 0, 0, 0.6); border: none; } /* Анимация появления*/ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
Далее сложно: JavaScript. Чтобы уменьшить количество вашего взаимодействия с JavaScript, был снова сделан модуль, работающий через DATA-атрибуты. Всё, как и в предыдущий раз. С ним можно ознакомиться в коде примера:
Запустить примерНе изобретай велосипед
Часто, лучшим способом является не создание собственного "велосипеда", а использование уже готового продукта. Модальные окна здесь не исключение.Можно просто найти хороший модуль модальных окон, прочитать к нему документацию и использовать его. Тогда практически не придется задумываться насчёт JavaScript и вёрстки окна. Примером могут послужить Tingle или Micromodal.
Вёрстка чекбоксов
Вёрстка почти каждой формы предполагает изменение вида стандартных чекбоксов. Изначально, они выглядят вот так: и редко когда вписываются в дизайн. Поэтому каждый верстальщик знает, как кастомизировать чекбоксы. Сейчас и мы научимся это делать.
Вкратце, наша задача - использовать label со скрытым внутри чекбоксом. Дело в том, что в HTML клик по label переадресует его на внутренний элемент input. Кроме того, label можно использовать с атрибутом for, значением которого выступает id элемента input, находящегося снаружи нашего label, но применения этой конструкции я нашел лишь пару раз в своей практике.
Немного подробнее о label можно почитать здесь
В этом видео Вадим Макеев подробно рассказывает об этой технике вёрстки чекбокса:
Пример:
Код:
<p> <label class="custom-checkbox"> <input class="visually-hidden" type="checkbox"><span class="checker"></span>Checkbox </label> <label class="custom-checkbox"> <input class="visually-hidden" type="checkbox" checked><span class="checker"></span>Checked </label> <label class="custom-checkbox"> <input class="visually-hidden" type="checkbox" disabled><span class="checker"></span>Disabled </label> <label class="custom-checkbox"> <input class="visually-hidden" type="checkbox" checked disabled><span class="checker"></span>Checked disabled </label> </p>
.custom-checkbox { /* Контейнер должен быть релятивным, так как внутри него мы разместим два абсолютно спозиционированных элемента */ position: relative; /* скрываем элементы, попадающие за границы label */ overflow: hidden; /* По умолчанию, label - это строчный элемент. С высокой вероятностью, нам нужно будет добавлять вертикальный margin, поэтому сразу сделаем его строчно-блочным*/ display: inline-block; /* Делаем так, чтобы чекбокс нельзя было выделить курсором, а только нажать */ user-select: none; /* Я приверженец того, что все интерактивные элементы должны иметь cursor: pointer, поэтому задаём и его */ cursor: pointer; /* Отодвигаем левую часть так, чтобы влез кастомный чекбокс*/ padding: 0 1em 0 1.4em; /* Задаём минимальную высоту */ min-height: 1em; } /* Задаем кастомную галочку для чекбокса и скрываем её по умолчанию */ .custom-checkbox .checker { position: absolute; margin-top: 0; /* Выдвигаем чекбокс левее так, чтобы он не наезжал на текст */ margin-left: -1.2em; width: 1em; height: 1em; overflow: hidden; text-align: center; font-size: 1em; line-height: 1; border: 1px solid teal; border-radius: 3px; background-color: white; /* Делаем галочку прозрачной */ color: transparent; } /* В псевдо-элементе допишем саму галочку */ .custom-checkbox .checker:after { content: '✓'; } /* указываем селектор на наш конкретный инпут с типом чекбокс */ .custom-checkbox input[type='checkbox'] { /* Выводим стандартный чекбокс за границы label */ position: absolute; right: 100%; top: 0; } /* Несколько сложноее CSS правило: когда чекбокс будет иметь атрибут checked, его сосед снизу (селектор +) с классом .checker, примет эти стили */ .custom-checkbox input[type='checkbox']:checked + .checker { /* Перекрашиваем фон чекбокса и галочку */ background-color: teal; color: white; } /* Не все люди используют для просмотра веб-страниц мышь, а некоторые из них используют вообще только клавиатуру. Так вот, чтобы интерактивные элементы подсвечивались, когда фокус клавиатуры попадал на них, им нужно задать отдельные стили. */ /* Когда скрытый инпут окажется в фокусе, его сосед с классом .checker примет стили */ .custom-checkbox input[type='checkbox']:focus + .checker { box-shadow: 0 0 5px 0 teal; } /* Когда скрытый интуп нельзя изменить, его сосед с классом .checker примет стили */ .custom-checkbox input[type='checkbox']:disabled+.checker { background-color: #999; border-color: #999; } /* Этот класс позволяет правильно скрыть элемент с экрана так, чтобы он был доступен для скринридеров */ .visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; border: 0; padding: 0; clip: rect(0 0 0 0); overflow: hidden; }
Подробнее о visually-hidden можно почитать здесь: How to hide elements visually
Задача
Сверстать чекбоксы как в gmail.
.sol4 { display: flex; align-items: center; flex-direction: column; } .sol4 .row { border-bottom: 1px solid lightgray; border-top: 1px solid lightgray; padding: 8px; margin-bottom: -1px; display: block; line-height: 1; background: white; } .sol4 .custom-checkbox { padding-right: 0; vertical-align: middle; } .sol4 .custom-checkbox .checker::after { display: none; } .sol4 .custom-checkbox input[type='checkbox']:focus + .checker { box-shadow: none; } .sol4 .custom-checkbox .checker { background-repeat: no-repeat; background-position: center center; border: none; opacity: 0.3; } .sol4 .custom-checkbox input[type='checkbox']:checked + .checker { background-color: transparent; opacity: 1; } .sol4 .custom-checkbox.default .checker { background-image: url('https://www.gstatic.com/images/icons/material/system/1x/check_box_outline_blank_black_20dp.png'); } .sol4 .custom-checkbox.default input[type='checkbox']:checked + .checker { background-image: url('https://www.gstatic.com/images/icons/material/system/1x/check_box_black_20dp.png'); } .sol4 .custom-checkbox.star .checker { background-image: url('https://www.gstatic.com/images/icons/material/system/1x/star_border_black_20dp.png'); } .sol4 .custom-checkbox.star input[type='checkbox']:checked + .checker { background-image: url('https://www.gstatic.com/images/icons/material/system/1x/star_googyellow500_20dp.png'); } .sol4 .custom-checkbox.label .checker { background-image: url('https://www.gstatic.com/images/icons/material/system/1x/label_important_outline_black_20dp.png'); } .sol4 .custom-checkbox.label input[type='checkbox']:checked + .checker { background-image: url('https://www.gstatic.com/images/icons/material/system/1x/label_important_googyellow500_20dp.png'); }
<div class="sol4"> <div class="row"> <label class="custom-checkbox default"> <input class="visually-hidden" type="checkbox"><span class="checker"></span> </label> <label class="custom-checkbox star"> <input class="visually-hidden" type="checkbox"><span class="checker"></span> </label> <label class="custom-checkbox label"> <input class="visually-hidden" type="checkbox"><span class="checker"></span> </label> </div> <div class="row"> <label class="custom-checkbox default"> <input class="visually-hidden" type="checkbox" checked><span class="checker"></span> </label> <label class="custom-checkbox star"> <input class="visually-hidden" type="checkbox" checked><span class="checker"></span> </label> <label class="custom-checkbox label"> <input class="visually-hidden" type="checkbox" checked><span class="checker"></span> </label> </div> </div>
Картинки для чекбоксов взять отсюда:
Audio и Video
К этому моменту вы уже точно познакомились со вставкой изображений в HTML-документ. А что с медиа-файлами? Как сервисы позволяют нам слушать музыку и смотреть фильмы прямо на сайте? Раньше для этого было необходимо встраивать в страницу Flash Player от Adobe. На данный момент, все корпорации отказываются от этого способа и слишком поздно его рассматривать.
<audio>
Благодаря тегу audio, мы можем воспроизводить звуки на странице. Этот тег можно использовать как одиночно, так и совместно с тегом source. В тегах source можно указывать разные одинаковые звуки в разных форматах для обеспечения наибольшей поддержки. Звук может быть представлен в одном из следующих форматов: mp3, ogg, wav, aac. Наибольшую поддержку на данный момент имеет формат mp3. Рассмотрим примеры:
<audio src="sound.mp3" controls></audio> <audio contols autoplay> <source src="sound.mp3" type="audio/mpeg" > <source src="sound.ogg " type="audio/ogg" > </audio>
Как видите, тег source обязан иметь путь к файлу в атрибуте src, а так же mime-тип, содержащийся в атрибуте type. Если использовать тег audio без source, то он обязан иметь атрибут src, значением которого будет путь к файлу.
Кроме того, тег audio может содержать следующие атрибуты:
- controls - добавляет элементы управления (пауза/воспроизведение, громкость, пр.);
- autoplay - проигрывает звук после загрузки страницы;
- loop - циклично повторяет звук;
- preload - предзагружает аудиофайл до того, как пользователь нажал на воспроизведение.
<video>
Как это следует из названия, тег video служит для вставки видео на страницу. В отличие от тега audio, его нельзя использовать без source.
<video controls autoplay muted> <source src="movie.mp4" type="video/mp4"> <source src="movie.webm" type="video/webm"> </video>
Здесь всё очень похоже на то, что есть в теге audio, но поддерживаемых атрибутов больше. Рассмотрим некоторые из них:
- controls - добавляет элементы управления (пауза/воспроизведение, громкость, на весь экран, пр.);
- autoplay - проигрывает видео после загрузки страницы (почти никогда не работает без атрибута muted);
- muted - воспроизводит видео без звука;
- loop - циклично проигрывает видео;
- preload - предзагружает видеофайл до того, как пользователь нажал на воспроизведение;
- poster - позволяет указать изображение заставки.
Существует три наиболее поддерживаемых видео-формата: mp4, webm, ogv, наибольшее распространение среди которых получил mp4, незначительно обогнав webm.
CSS и пропорции
Иногда требуется задать высоту видео ещё перед его загрузкой, притом, что ширина тоже неизвестна. К примеру, ширина видео будет равна ширине какого-то контейнера, а ширина этого контейнера зависит от кучи других параметров. Для решения этой проблемы, нам понадобится знать лишь соотношение сторон видеофайла. Почти всегда он равен 16/9 или 4/3, но бывают и исключения. Итак, как динамически вычислить высоту ролика до его загрузки, имея только его aspect-ratio (соотношение сторон)? Для этого будет необходимо создать для видео внешний контейнер с position: relative, самому тегу <video> задать position: absolute, а так же передать с помощью CSS-переменных соотношение сторон.
Рассмотрим код:
<div class="video" style="--aspect-ratio: calc(16 / 9)"> <video> <source src="....mp4" type="video/mp4"> </video> </div>
И CSS:
.video { position: relative; padding-top: calc(100% / var(--aspect-ratio, 1.77)); } .video video { position: absolute; width: 100%; top: 0; left: 0; right: 0; }
Что здесь происходит? Мы задаём padding-top или padding-bottom у контейнера, содержащего видео равным его ширине делённой на соотношение сторон. Дело в том, что в свойстве padding при задании значения в процентах, всегда берётся ширина этого блока. Именно из-за этого и работает наш пример.
Изучить его подробнее можно тут:
Посмотреть примерЧто ещё почитать?
Для более плотного ознакомления с этими тегами, рекомендую почитать MDN: <audio> и MDN: <video>.
Знаете ли вы?
В этой статье приведены нестандартные и неоднозначные решения тех или иных проблем, связанных с вёрсткой.
Знаете ли вы, что картинки можно хранить прямо в css-файле?
Небольшие картинки, используемые, например, в свойстве background-image, можно хранить прямо в css. Лучше всего это работает с SVG-графикой. Для того, чтобы вставить картинку в css, нам придется воспользоваться простеньким энкодером, который, к тому же, сразу же сгенерирует код для вставки в css.
Если же у вас растровая иконка и вы используете LESS, то обратите внимание на функцию data-uri, которая принимает в качестве параметра путь к файлу с картинкой, а скомпилированный css-файл уже будет содержать закодированную картинку.
Знаете ли вы, что можно совмещать CSS-variables с HSL и calc?
Мы можем создавать темы для сайта, основанные на CSS-переменных. Это будет выглядеть так: все цвета мы задаём на сайте не конкретно, а абстрактно, задавая формулу, по которой в дальнейшем будет вычисляться цвет. Так, изменяя всего три CSS-переменные, мы можем полностью перекрасить весь сайт. Цвета можно изменять на основе времени суток на часах пользователя или же в зависимости от его особенностей зрения. Для нас главная возможность — упростить с этим нашу работу. Задать три переменные проще, чем подключить огромный CSS-файл, который может содержать ошибки по сравнению с главной темой.
Чуть больше конкретики. Цвета в CSS можно задавать при помощи HEX-палитры (#c00), RGB-палитры (rgb(204,0,0)) и HSL-палитры (hsl(0,100%,40%)). Нас интересует сейчас именно палитра HSL. Она позволяет задавать цвет на основе трёх составляющих: Hue, Saturation и Lightness. Hue изменяет цвет от красного до фиолетового, Saturation изменяет насыщенность цвета, Lightness изменяет яркость цвета.
.theme-1 { --primary-h: 0; --primary-s: 75; --primary-l: 60; } .theme-2 { --primary-h: 200; --primary-s: 100; --primary-l: 65; } .palette { --primary: hsl( var(--primary-h), calc(var(--primary-s) * 1%), calc(var(--primary-l) * 1%) ); --primary-100: hsl( var(--primary-h), calc(var(--primary-s) * 1.4 * 1%), calc(var(--primary-l) * 1.4 * 1%) ); --primary-200: hsl( var(--primary-h), calc(var(--primary-s) * 1.3 * 1%), calc(var(--primary-l) * 1.3 * 1%) ); --primary-300: hsl( var(--primary-h), calc(var(--primary-s) * 1.2 * 1%), calc(var(--primary-l) * 1.2 * 1%) ); --primary-400: hsl( var(--primary-h), calc(var(--primary-s) * 1.1 * 1%), calc(var(--primary-l) * 1.1 * 1%) ); --primary-500: var(--primary); --primary-600: hsl( var(--primary-h), calc(var(--primary-s) * 0.9 * 1%), calc(var(--primary-l) * 0.9 * 1%) ); --primary-700: hsl( var(--primary-h), calc(var(--primary-s) * 0.8 * 1%), calc(var(--primary-l) * 0.8 * 1%) ); --primary-800: hsl( var(--primary-h), calc(var(--primary-s) * 0.7 * 1%), calc(var(--primary-l) * 0.7 * 1%) ); --primary-900: hsl( var(--primary-h), calc(var(--primary-s) * 0.6 * 1%), calc(var(--primary-l) * 0.6 * 1%) ); } ul { display: flex; list-style: none; padding: 0; margin: 0 0 20px; } ul li { display: block; padding: 20px; margin-right: 5px; color: white; text-shadow: 0 0 5px black; }
<div class="theme-1"> Theme 1 <ul class="palette"> <li style="background: var(--primary-100)">100</li> <li style="background: var(--primary-200)">200</li> <li style="background: var(--primary-300)">300</li> <li style="background: var(--primary-400)">400</li> <li style="background: var(--primary-500)">500</li> <li style="background: var(--primary-600)">600</li> <li style="background: var(--primary-700)">700</li> <li style="background: var(--primary-800)">800</li> <li style="background: var(--primary-900)">900</li> </ul> </div> <div class="theme-2"> Theme 2 <ul class="palette"> <li style="background: var(--primary-100)">100</li> <li style="background: var(--primary-200)">200</li> <li style="background: var(--primary-300)">300</li> <li style="background: var(--primary-400)">400</li> <li style="background: var(--primary-500)">500</li> <li style="background: var(--primary-600)">600</li> <li style="background: var(--primary-700)">700</li> <li style="background: var(--primary-800)">800</li> <li style="background: var(--primary-900)">900</li> </ul> </div>
Практика
Настало время практики. попробуем сверстать простенький сайт, который, возможно, послужит вам первым резюме. Сразу оговорюсь по поводу картинок — в этом примере используется генерация людей нейросетью, этих людей никогда не существовало. Если вы увидите что-то необычное или пугающее, просто обновите страницу, и, возможно, в этот раз нейросеть сгенерирует что-то более человечное. Описание на русском.
Вы можете изменять размер вьюпорта (мобильный вид / планшетный / ноутбук / монитор), потянув за правый нижний угол или используя переключатели сверху. По итогу, должно получиться примерно так:
Разметка
Итак, приступим к вёрстке. Первым делом необходимо создать HTML-разметку. И сразу же подключим необходимые нам
шрифты с
Google Fonts. Мы будем использовать шрифт Open Sans.
Кроме того, мы будем использовать иконки из пака
Font awesome.
Ну и сразу же создадим и подключим файл стилей. Назовём его, к примеру,style.css
.
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Портфолио</title> <link rel="stylesheet" href="styles.css" /> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600&display=swap&subset=cyrillic" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css" /> </head> <body> </body> </html>
Возможно, у вас возникли некоторые вопросы по нашей отправной точке. Постараюсь ответить на них
последовательно.<html lang="ru">
в этой строке мы указываем язык страницы.
<meta charset="UTF-8" />
означает, что кодировка страницы
UTF-8.
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
указывает, что сайт является адаптивным и самостоятельно подстроится под дисплей любого размера.
Сразу подумаем, как должен выглядеть будущий сайт как на мобильных устройствах, так и на устройствах с большим дисплеем. Судя по макету, максимальная ширина сайта — 1200px, а если дисплей шире, то сайт выстраивается посередине. Самый простой способ сделать так — использовать какой-то внешний контейнер. Внутри этого контейнера будет блок с краткой информацией о человеке и блок с основным контентом.
<body> <main class="container"> <div class="info">...</div> <div class="content">...</div> </main> </body>
Теперь посмотрим, что у нас есть в блоке с информацией (.info). Там есть фото, его имя, и три пары заголовок-контент. Попробуем это воссоздать.
<div class="info"> <div class="photo"> <img ... /> </div> <h1>...</h1> <section> <h2>...</h2> ... </section> <section> <h2>...</h2> ... </section> <section> <h2>...</h2> ... </section> </div>
Блок с основным контентом представляет собой четыре пары заголовок-список. Так и напишем в коде:
<div class="content"> <section> <h2>...</h2> <ul>...</ul> </section> <section> <h2>...</h2> <ul>...</ul> </section> <section> <h2>...</h2> <ul>...</ul> </section> <section> <h2>...</h2> <ul>...</ul> </section> </div>
Начальная стилизация
Приступим, наконец, к стилям. Прежде всего, нужно определиться с цветами, шрифтами и прочим. Используем CSS-переменные и занесём туда все цвета макета, примем border-box в качестве правила хорошего тона (об этом читай в статье Блочная модель и box-sizing), установим для странички основным шрифтом Open Sans, кегль шрифта — 16px (берём за основу кегль основного текста в макете), цвет шрифта сразу же установим согласно цвету основного текста в макете. Также обнулим значение margin по умолчанию для body.
:root { --back: #404042; --accent: #e61d31; --middle: #bbb; --light: #f2f2f2; } * { box-sizing: border-box; } body { margin: 0; font-family: 'Open Sans', sans-serif; font-size: 16px; color: var(--back); }
Как мы и говорили раньше, максимальный размер контейнера — 1200px. Блоку с информацией присвоим тёмный фон, согласно макету, светлый цвет текста, внутренний отступ (можете подобрать любое значение, лишь бы потом не забыть сделать как по макету). Блоку с основным контентом тоже присвоим какой-то внутренний отступ и белый фон.
.container { max-width: 1200px; } .info { background-color: var(--back); color: var(--light); padding: 1rem; } .content { background-color: white; padding: 1rem; }
И сразу же зададим стили для абзацев и ссылок:
p { font-size: 1rem; margin: 0 0 0.5em; } a { text-decoration: none; } a:hover { text-decoration: underline; } a .fab, a .fas { margin-right: 0.5em; }
Вёрстка блоков
Существует два основных подхода к вёрстке адаптивных (подстраивающихся под устройство) сайтов: mobile-first и desktop-first. Первый считается общепризнанным стандартом, так как позволяет, теоретически, упростить вычисление CSS для мобильных устройств. Дело в том, что мобильные телефоны считаются более слабыми по сравнению с компьютерами. В случае, когда мы используем подход mobile-first, сайт изначально выглядит так, как нужно мобильным устройствам, а если ширина экрана это позволяет, подключает дополнительные CSS-правила, позволяющие расположить элементы, используя большую ширину экрана, нежели у телефона. И так, постепенно наращивая количество правил, включающихся по мере роста ширины экрана, раскладка становится подходящей для широких экранов компьютера. В подходе desktop-first происходит наоборот — сначала сайт ведет себя как сайт для полноценных компьютеров, а если ширины экрана недостаточно, подключает стили, изменяющие раскладку элементов на раскладку, подходящую для мобильных устройств. От этой дополнительной вычислительной нагрузки на мобильных устройствах принято отказываться по двум причинам:
- как было сказано выше, мобильные устройства не так производительны;
- более половины пользователей интернета используют, в первую очередь, мобильные устройства.
Именно поэтому мы будем сначала верстать под мобильные устройства, а затем постепенно увеличивать экран.
Блок с информацией
Как видно на макете, цвета ссылок, а так же толщина шрифта ссылок и абзацев в блоке с тёмным фоном отличаются от этих параметров в блоке с основным контентом. Добавим для этого дополнительные стили:
.info a { color: var(--light); font-weight: 300; } .info h2 { font-weight: 300; }
Фото профиля
Приступим к вёрстке отдельных блоков. Как было сказано выше, автор использует фотографии несуществующих людей, сгенерированные нейросетью. Ссылку на сгенерированную фотографию можно увидеть в коде. Обязательно указываем атрибут alt с описанием, что это за изображение.
<div class="info"> <div class="photo"> <img src="https://thispersondoesnotexist.com/image" alt="Фото профиля" /> </div> ... </div>
.photo { padding: 1rem; } .photo img { width: 100%; display: block; border-radius: 50%; filter: grayscale(1) contrast(1.2); }
Здесь мы указываем просто оформляем блок с фото. Чтобы квадратное фото сделать круглым, нужно задать border-radius: 50%. Однако, если фото не будет квадратным, то рекомендую указать значение этого свойства в пикселях, дабы не получить овал. В этом проекте автор использует неподготовленные для сайта фотографии, поэтому было принято решение обработать их согласно специфике сайта. Для этого используется CSS Filter, о которых мы говорили в главе Градиенты, тени и фильтры.
Заголовок
Вёрстка этого блока довольно проста: имя человека и его позиция является заголовком первого уровня. Чтобы
отделить
перенести должность на другую строку, нужно вынести её в отдельный тег и использоватьdisplay: block
.
<h1> Дэми Браун <small>Junior front-end developer</small> </h1>
h1 { font-size: 2rem; font-weight: 300; margin: 0 0 0.2em; line-height: 1; text-align: center; color: var(--accent); text-transform: uppercase; } h1 small { display: block; font-size: 1rem; color: white; font-weight: 300; padding: 1em 0; }
Секция с контактной информацией
Эта секция состоит из заголовка и списка ссылок с иконками. Вёрстка так же предельно проста:
<section class="contacts"> <h2>Контакты</h2> <ul> <li> <a href="//linkedin.com" target="_blank" rel="noreferrer"> <i class="fab fa-linkedin" aria-hidden="true"></i> LinkedIn </a> </li> ... </ul> </section>
Задачей CSS является просто немного подвигать элементы и задать размеры иконкам:
.contacts .fab, .contacts .fas { font-size: 1.5em; vertical-align: middle; } .contacts ul { margin: 0; padding: 0; list-style: none; } .contacts li { margin-bottom: 1.5em; } h2 { font-size: 1.5rem; padding: 0 0 0.7em; margin: 2.5em 0 1em; border-bottom: 2px solid var(--middle); text-transform: uppercase; font-weight: 600; position: relative; } h2::after { content: ''; display: block; position: absolute; bottom: -2px; left: 0; width: 4rem; border-bottom: 0.6rem solid var(--accent); }
Заголовок
Заголовок второго уровня имеет необычный элемент красного цвета под собой. Он спозиционирован относительно заголовка и представляет собой красный прямоугольник, находящийся на той же высоте, что и border заголовка.
Пара слов о ссылках
Оформление ссылок — это важная задача, игнорировать которую ни в коем случае нельзя, ведь это косвенно влияет на приток клиентов. К примеру, если у нас будет указан номер телефона, по нажатию на который ничего не произойдёт, мало какой клиент начнёт переписывать его в свой телефон для вызова.
Для оформления ссылок на сторонние ресурсы, необходимо добавлять два атрибута у ссылок: target и rel. Атрибут target позволяет управлять поведением открытия страницы по ссылке. К примеру, значение этого атрибута _blank откроет страницу в новой вкладке. Именно его и нужно использовать для указания ссылок на сторонние ресурсы. Кроме того, необходимо указывать атрибут rel со значением noreferrer или nofollow. Этот атрибут говорит поисковым машинам, что ссылка ведёт на сторонний ресурс, индексировать который нам не обязательно.
Оформление номера телефона обязательно сопровождается ссылкой, атрибут href которой начинается с tel. Телефон указывается в международном формате. К примеру,
<a href="tel:+12345678900" rel="noreferrer"> +1(234) 567-89-00 </a>
Чтобы оформить ссылку на почту, используйте этот формат:
<a href="mailto:mail@gmail.com" rel="noreferrer"> mail@gmail.com </a>
Также можно оформлять ссылки на чат, к примеру, в WhatsApp, Telegram или Viber:
<a href="https://wa.me/1234567890" rel="noreferrer"> WhatsApp (+1234567890)</a>
<a href="tg://resolve?domain=YOUR_NICKNAME" rel="noreferrer"> Telegram (@YOUR_NICKNAME)</a>
Секции навыков и знания языков
Эта секция представляет собой заголовок и пары название-освоение. Ничего сложного:
<section class="skills"> <h2>Навыки</h2> <figure class="skill"> <figcaption class="title">HTML</figcaption> <svg class="progress" aria-label="50%"> <rect class="bar" style="width: 50%"></rect> </svg> </figure> ... </section>
.skills .skill { display: grid; grid-template-columns: 1fr 2fr; gap: 1rem; align-items: center; margin: 0 0 1em; } .skills .skill .title { font-weight: 300; } .progress { background-color: var(--middle); width: 100%; height: 4px; } .progress .bar { fill: var(--accent); height: 100%; }
Разберёмся подробнее. Каждая фигура<figure class="skill">
является гридом. Внутри находится
название<figcaption class="title">
и сама фигура, в данном случае, являющаяся inline-svg:<svg
class="progress">
. По умолчанию SVG имеем какой-то размер, поэтому мы устанавливаем его своим:
.progress { width: 100%; height: 4px; }
А чтобы перекрасить сам столбик прогресса, мы должны воспользоваться свойствомfill
, так как
SVG-элементы не имеют свойстваbackground-color
.
Основной контент
В этой части практики мы поговорим о блоке с контентом. Он состоит из четырёх секций: опыт, образование, интересы и проекты.
Секции "Опыт" и "Образование"
В секции "Опыт", помимо заголовка, находится список мест работы. Элементы списка содержат позицию как сотрудника компании, даты начала и окончания трудового договора, название компании, достижения и обязанности сотрудника.
Секция "Образование" представляет собой точно такую же вёрстку, но другую информацию: вместо позиции — получаемую учёную степень; вместо дат трудового договора — даты начала и конца обучения; вместо названия компании — название ВУЗа; вместо обязанностей — кратко о полученных знаниях.
<div class="content"> <section> <h2>Опыт</h2> <ul class="experience"> <li> <div class="short-info"> <div class="position">Junior front end developer</div> <div class="date-range"> <time>Сентябрь 2019</time> — <time>По настоящее время</time> </div> <div class="company">ООО «Рога и копыта»</div> </div> <div class="responsibilities"> <p> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Deserunt minima temporibus ratione quae dolorem accusamus doloribus consectetur dolor in aut at vel repellat rerum praesentium voluptate libero, eveniet pariatur? Quos? </p> </div> </li> ... </ul> </section> <section> <h2>Образование</h2> ... </section> ... </div>
.experience li { display: block; margin-bottom: 2rem; } .experience .short-info { margin-bottom: 1rem; } .experience .position { font-weight: 600; } .experience .date-range time { font-size: 0.7rem; }
Надеюсь, этот блок не доставит вам больших проблем в понимании.
Интересы
Этот блок состоит из списка иконок. Как мы понимаем, для незрячих людей, использующих скринридер, иконки это
ничто.
Поэтому им необходимо задать описание. Кроме того стоит подумать о том, как объяснить человеку, что скрывается за
иконкой футбольного мяча? Футбол? Спорт? Мяч? Что-то ещё? Убить двух зайцев нам поможет атрибутtitle
.
Посмотрим, как это будет выглядеть:
<section> <h2>Интересы</h2> <ul class="interests"> <li> <i class="fas fa-futbol" title="Спорт"></i> </li> <li> <i class="fas fa-camera-retro" title="Фотография"></i> </li> <li> <i class="fas fa-guitar" title="Музыка"></i> </li> <li> <i class="fas fa-suitcase-rolling" title="Путешествия"></i> </li> </ul> </section>
.interests { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; font-size: 3rem; opacity: 0.8; } .interests li { margin-right: 1rem; }
Простое оформление списка в одну строку и немного стилей для размещения иконок.
Секция "Проекты"
Последняя часть разметки — секция "Проекты". Она содержит заголовок и список проектов, представляющих собой иконку, заголовок проекта с ссылкой на страницу проекта и описание.
<section> <h2>Проекты</h2> <ul class="projects"> <li> <i class="fas fa-file-code" aria-hidden="true"></i> <h3> <a href="#"> Проект </a> </h3> <p>Описание проекта</p> </li> ... </ul> </section>
h3 { font-size: 1.2rem; margin: 0 0 0.5em; } .projects { list-style: none; margin: 0; padding: 0; } .projects li { margin-bottom: 1em; position: relative; padding-left: 3em; } .projects a { color: var(--back); } .projects .fas { position: absolute; top: 0.2em; left: 0; font-size: 2.5em; opacity: 0.8; }
Так как название и описание проекта находится в отдельных тегах, для слабовидящих людей иконка не несёт никакой
смысловой нагрузки, поэтому её можно скрыть атрибутом aria-hidden="true"
. Для зрячих же людей,
спозиционируем картинку относительно элемента списка, представляющего собой проект. Мы могли бы также
воспользоваться и flex-контейнером.
Адаптивность и отзывчивость
К этому моменту у нас уже есть полностью функционирующая мобильная версия проекта. Однако, на этом этапе, она
выглядит хорошо только до размера экрана примерно в 500px.Нам нужно создать медиа-выражение, изменяющее
расположение
блоков при достижении ширины экрана 500px.
Всё, что должно произойти к этому моменту — .info
должен оказаться в левой части экрана, а
блок .content
— в правой.
Как мы помним, HTML выглядит так:
<body> <main class="container"> <div class="info">...</div> <div class="content">...</div> </main> </body>
Чтобы эти блоки расположились рядом, нужно прочитать статью Многоколоночные макетыи использовать один из представленных там методов. Для примера, я использую гриды:
@media (min-width: 500px) { { display: grid; grid-template-columns: 220px 1fr; } }
Э
тот код читать нужно так: если ширина экрана более 500 пикселей, то блок .container
станет гридом с
раскладкой 200px 1fr
.
Увеличим ширину экрана ещё. На отметке примерно в 700px мы снова увидим, что вёрстка выглядит не очень хорошо. Создадим ещё одно правило:
@media (min-width: 700px) { body { background: linear-gradient(135deg, rgba(240, 240, 240, 1) 50%, rgba(225, 225, 225, 1) 50%); } { grid-template-columns: 2fr 3fr; } .info, .content { padding: 3rem; } }
При достижении ширины экрана в 700px, установим на фон страницы градиент, изменим раскладку грида
блока.container
и зададим большие отступы для его дочерних блоков.
Последние две точки адаптивности посвятим блокам.experience
и блоку.container
.
@media (min-width: 900px) { .experience li { display: grid; grid-template-columns: 2fr 3fr; gap: 2rem; } } @media (min-width: 1200px) { { margin: 5vh auto; box-shadow: 30px 50px 40px 20px rgba(0, 0, 0, 0.3); } }
Заключение
Таким образом, нам удалось сверстать простенькую страничку, которая может стать вашим стартом и первым проектом в области Font end разработки.