Делать сайты я учился по-старомодному: открывал исходный код и пытался повторить то, что видел. А по тому, чего не видел (PHP/MySQL), прочёл какую-то случайно попавшуюся книгу. Так всё и шло. Это было в 1999 году, когда можно было писать что-то вроде <font size=”4" color=”#000000">
, а DHTML приобретал популярность.
Когда пришла пора CSS, мой подход к обучению не изменился. Но сейчас мне действительно жаль, что тогда я не уделил CSS должного внимания и упустил из виду столько принципиально важных вещей. Я расскажу о том, о чём мне следовало бы узнать раньше.
Хотя мне было известно об этих свойствах, их глубокого понимания у меня не было. Вот основная информация:
block
) элементы меняют размер в горизонтальной плоскости, занимая всю ширину строки (например, заголовок). Таким элементам можно задать вертикальный margin.inline
) элементы меняют размер в горизонтальной плоскости ровно настолько, чтобы вместить свой контент (например, strong
и em
). Таким элементам нельзя задать вертикальный внешний отступ (margin), и обычно они размещаются внутри блочных элементов.inline-block
) элементы похожи на строчные, но им можно задать вертикальный внешний отступ (margin). Такие элементы полезно применять, например, для оформления кнопок.В примере ниже у блочных элементов синие границы, у строчных — оранжевый фон, а у строчно-блочного — красные границы:
Само по себе то, что изображения по умолчанию являются строчными элементами, — не проблема, но это может привести к путанице при попытке их позиционировать или задать вертикальный внешний отступ. Если в ваших общих стилях ещё нет правила, превращающего изображение в блочный элемент, то я посоветовал бы вам его добавить:
img {
display: block;
}
Такое объявление делает поведение изображений гораздо более предсказуемым. Также можно задать изображениям max-width: 100%;
, чтобы они не выходили за пределы контейнера.
По умолчанию высота (height
) и ширина (width
) контейнера вычисляется путём сложения следующих измерений:
Такой подход обычно не вызывает проблем, когда дело касается высоты элемента: изменение вертикального положения контента обычно не особо нас волнует. Трудности начинаются при попытке вычислить ширину элемента, особенно если их в ряду больше одного. Если бы в CSS было такое объявление:
.some-class {
width: 50%;
padding: 2em;
border: 0.1rem;
}
Тогда полная ширина .some-class
рассчитывалась бы таким образом: 50% + 4em + 0.2rem
. Так происходит из-за свойства box-sizing
, которое по умолчанию имеет значение content-box
. Это значит, что свойство width
относится только к области контента, всё остальное идёт дополнительно. Значение box-sizing
можно изменить для всех элементов, вот так:
* {
box-sizing: border-box;
}
Возвращаясь к примеру, ширина элемента теперь включает и его границы, поэтому полная ширина в нашем случае равна 50%
.
А что с border
и padding
? Эти свойства всё ещё применяются, но не влияют на полную ширину элемента — для них определена отдельная область. Вот, что это означает на практике:
Мы не обсуждали здесь margin
, поскольку margin — это отступ между элементами. То есть его не нужно включать в расчёт ширины по определению.
Если у элемента нет фона и границ, может показаться, что padding
и margin
— это одно и то же, но это не так.
margin
— отступ между элементамиpadding
— отступ внутри элементаpadding
оказывается очень кстати в элементах, у которых есть фон. Обычно нам нужно, чтобы контент располагался чуть дальше от краев контейнера элемента, и padding
помогает нам обозначить эти края.
Внешние отступы схлопываются, и это долго расстраивало новичков в CSS. Рейчел Эндрю описывает это поведение так:
Когда внешние отступы схлопываются, то складываются таким образом, что расстояние между двумя элементами равно большему из отступов. Таким образом, меньший отступ оказывается внутри большего.
Если у нас есть два блочных элемента, один из которых имеет свойство margin-bottom: 1em
, а второй — ниже первого — имеет свойство margin-top: 1.5em
), то отступ между этими элементами равен 1.5em
. Смотрим пример ниже:
Зная об этой особенности, мы можем быстро и просто вычислять внешние отступы. Понимание схлопывания может изменить и наш подход к отступам в целом — здесь может пригодиться что-то вроде селектора “лоботомированной совы”: * + *
.
Примечание: внешние отступы не схлопываются, когда у родительского элемента есть свойство display: grid
или display: flex
.
CSS расшифровывается как Cascading Style Sheets — каскадные таблицы стилей. Неудивительно, что каскадность — один из основных принципов CSS.
Теперь мы больше понимаем о том, как наши собственные файлы стилей взаимодействуют между собой, но также необходимо помнить, что у браузера всегда есть свои стили по умолчанию. Они загружаются до пользовательских стилей, что позволяет проще переопределять текущие значения.
Стили по умолчанию отличаются от браузера к браузеру, но именно они — причина того, что:
display
(например, block
или inline
).Этот список можно продолжать. Даже если у сайта всего одна таблица стилей, она всё равно смешивается с браузерными стилями по умолчанию.
Пиксели (px
) заманчивы, их просто понять: объяви font-size: 24px
— и размер текста станет равен 24px
. Однако опыт взаимодействия с таким интерфейсом будет не самым лучшим, особенно для пользователей, самостоятельно меняющих размеры контента в браузере.
Я довольно быстро начал пользоваться em
, а позже и rem
для указания размера шрифта. Чтобы привыкнуть к em
и rem
в других элементах (padding
, margin
, letter-spacing
и border
), понадобилось гораздо больше времени.
Понимание разницы между em
и rem
— ключевой фактор в управления относительными единицами. К примеру, em
можно использовать для медиа-запросов (@media
) и вертикальных внешних отступов (margin
), а rem
для постоянной ширины границы(border-width
).
Применение относительных размеров требует небольшой перестройки мышления, но польза, получаемая в результате, определённо того стоит.
При использовании псевдоэлементов ::before
и ::after
необходимо указывать свойство content
, даже если оно не имеет значения:
.some-class::before {
content: '';
}
Если не указать content
, псевдоэлемент просто не показывается на странице.
Чтобы задать элементу ширину, основываясь на примерном количестве символов в строке, полезно знать о единицу ch
(character). Почему на примерном количестве? Потому, что ch
не вычисляет число символов в строке. Основа этой единицы — ширина символа 0
. Вот, что писал об этом Эрик Мейер:
“1ch обычно шире средней ширины символа примерно на 20–30%”.
Если вы используете эту единицу для контроля размеров абзацев или чего-то подобного, то важно знать о такой её особенности.
Этот термин я часто слышал, но долгое время не понимал в полной мере. “Нормальный поток” означает, что элементы на странице появляются в том же порядке, в каком они стоят в коде. Например, напишем:
<h2>Heading</h2>
<p>Paragraph text.</p>
В этом случае мы ожидаем, что <h2>Heading</h2>
появится перед/над <p>Paragraph text.</p>
. Это и есть нормальный поток.
Если же элемент вырывается из нормального потока, это значит, что он не появится в ожидаемом месте. Элементы, позиционированные с помощью float
или position: absolute
, — хорошие тому примеры.
О псевдоклассах :hover
, :focus
и :active
я узнал в контексте стилизации ссылок. В то время все примеры, которые я видел, выглядели примерно так:
a {
color: black;
}
a:hover,
a:focus,
a:active {
color: red;
}
Но будет лучше, если мы стилизуем состояние :focus
по-другому.
:focus
— это состояние, когда пользователь передвигается по фокусируемым элементам (например, ссылкам) с помощью клавиши табуляции. Когда пользователь нажимает TAB, он не знает, где окажется фокус. Кроме того, если пользователь сфокусируется на элементе, на который уже навёл курсор, он не узнает, где находится фокус. По этим причинам :focus
лучше стилизовать отдельно от :hover
и :active:
:
a:hover,
a:active {
/* styles */
}
a:focus {
/* styles */
}
Взгляните на этот пример
Заметили, что фон задан у нечётных строк? Учитывая селектор (p:nth-child(even)
), мы могли ожидать, что фон будет у чётных.
Однако селектор :nth-child()
считает все сестринские элементы. Указание элемента в селекторе (например, p:nth-child()
) вовсе не означает, что селектор начнёт считать с первого элемента именно этого типа. Напротив, правило будет применяться только к элементу такого типа. Если мы перепишем правило на p:nth-child(odd)
, то увидим, что:
h1
не имеет фона, хотя это нечётный сестринский элемент;p
, которые соответствуют критерию (второй, четвёртый, шестой), имеют фон.Возвращаясь к первому примеру, давайте представим, что мы хотим, чтобы фон был у чётных элементов p
. В этом случае лучше подойдёт другой псевдокласс — p:nth-of-type(even)
.
Этот пример показывает ключевое различие между :nth-child()
и :nth-of-type()
. Разница едва заметна, но знание о ней помогает избегать путаницы.
Основами CSS овладеть просто, но для написания более качественного кода крайне важно понимать, как CSS работает и почему. Изучение механизмов стилизации помогает мне писать стили быстрее, а также делает мой код более рациональным и устойчивым к ошибкам.
Перевод статьи
Комментарии