Учебное пособие по CSS-верстке: от классических подходов к новейшим технологиям
Опубликовано: 2022-03-11Освоить веб-верстку без освоения CSS так же реально, как научиться плавать по суше. Но в отличие от плавания, которое, будучи однажды освоенным, остается с вами на всю жизнь, освоение CSS — это процесс, который никогда не заканчивается, поскольку сам CSS постоянно развивается.
Проблема усугубляется различиями в реализации и поддержке CSS в разных браузерах (и даже в разных версиях одного и того же браузера), а также разной скоростью принятия рекомендаций CSS. Уже более десяти лет веб-дизайнеры и разработчики борются со спорадическими и непостоянными всплесками дополнительных функций CSS3, поддерживаемых в каждой новой версии браузера.
Но как бы то ни было, освоение CSS является абсолютной необходимостью для любого солидного веб-дизайнера или разработчика. В этой статье вы познакомитесь с некоторыми фундаментальными принципами компоновки CSS, от классических приемов CSS2 до новейших подходов компоновки в CSS3.
ПРИМЕЧАНИЕ. Во всех примерах кода в этой статье используются элементы HTML5 и синтаксис Sass. Полный рабочий код можно клонировать с https://github.com/laureanoarcanio/css-layout-examples.
Пример использования
Один из лучших способов изучить технологию — иметь конкретный вариант использования, который вы пытаетесь поддержать, или конкретную проблему, которую вы пытаетесь решить. С этой целью мы сосредоточимся на сценарии использования с определенным набором требований.
Наш вариант использования состоит из макета веб-приложения с некоторым динамическим поведением. Он будет иметь фиксированные элементы на странице, такие как верхний и нижний колонтитулы, меню навигации и поднавигация, а также прокручиваемый раздел контента. Вот конкретные требования к макету:
- Базовая компоновка
- Верхний и нижний колонтитулы, меню навигации и вложенная навигация остаются фиксированными при прокрутке.
- Элементы навигации и вспомогательной навигации занимают все свободное пространство по вертикали.
- Раздел контента использует все оставшееся свободное место на странице и имеет прокручиваемую область.
- Динамическое поведение
- В меню навигации по умолчанию отображаются только значки, но его также можно развернуть для отображения текста (а затем его можно свернуть, чтобы снова отображались только значки).
- Варианты макета
- Некоторые страницы имеют вспомогательную навигацию рядом с меню навигации, а некоторые нет.
Учебное пособие по CSS с использованием классических методов CSS2
Для начала вот разметка HTML5, которую мы будем использовать в нашем примере реализации с использованием классического CSS:
<body class="layout-classic"> <header></header> <nav></nav> <aside></aside> <main></main> <footer></footer> </body>
Фиксированное позиционирование
В CSS2 мы можем получить фиксированные элементы на странице (верхний и нижний колонтитулы и т. д.), используя модель макета с позиционированием, которая использует фиксированное позиционирование.
Кроме того, мы будем использовать свойство CSS z-index
, чтобы наши фиксированные элементы оставались «поверх» другого содержимого на странице. Свойство z-index
указывает порядок элементов в стеке, где элемент с большим порядком в стеке всегда находится «поверх» элемента с меньшим порядком в стеке. Обратите внимание, что свойство z-index
работает только с позиционированными элементами. В нашем примере мы произвольно используем значение z-index
20 (что выше значения по умолчанию), чтобы наши фиксированные элементы оставались визуально на переднем плане.
Кроме того, мы установим свойство width
на 100%, что указывает браузеру использовать все доступное пространство по горизонтали для элемента.
#header, #footer { position: fixed; width: 100%; z-index: 20; } #header { top: 0; height: 5em; } #footer { bottom: 0; height: 3em; }
Итак, это верхний и нижний колонтитулы. А как насчет #nav
и #subnav
?
CSS-расширение
Для #nav
и #subnav
мы будем использовать немного более сложную технику, известную как расширение CSS , которую можно использовать при позиционировании элементов как фиксированных (т. е. в фиксированном положении на странице) или как абсолютных (т. е. в указанная позиция относительно его ближайшего предка или содержащего блока).
Расширение по вертикали достигается за счет установки фиксированного значения для свойства top
и bottom
элемента, поэтому элемент будет расширяться по вертикали, чтобы использовать оставшееся пространство по вертикали соответствующим образом. По сути, вы привязываете верхнюю часть элемента к определенному расстоянию от верхней части страницы, а нижнюю часть элемента к определенному расстоянию от нижней части страницы, поэтому элемент расширяется, чтобы заполнить все вертикальное пространство. между этими двумя точками.
Точно так же расширение по горизонтали достигается путем установки фиксированных значений свойств left
и right
элемента, поэтому элемент будет расширяться по горизонтали, чтобы использовать оставшееся горизонтальное пространство соответствующим образом.
Для нашего варианта использования нам нужно использовать вертикальное расширение.
#nav, #subnav { position: fixed; top: 6em; /* leave 1em margin below header */ bottom: 4em; /* leave 1em margin above footer */ z-index: 20; } #nav { left: 0; width: 5em; } #subnav { left: 6em; /* leave 1em margin to right of nav */ width: 13em; }
Позиционирование по умолчанию (статическое)
Основная область прокручиваемого содержимого может просто полагаться на позиционирование по умолчанию (статическое), благодаря чему элементы отображаются в том порядке, в котором они появляются в потоке документа. Поскольку все остальное на нашей странице находится в фиксированном положении, этот элемент — единственный, который находится в потоке документов. В результате все, что нам нужно сделать, чтобы правильно расположить его, — это указать его свойство margin
, чтобы избежать перекрытия с фиксированным заголовком, нижним колонтитулом и nav/subnav:
#main { margin: 6em 0 4em 20em; }
Таким образом, мы удовлетворили основные требования к макету нашего варианта использования с использованием CSS2, но нам все еще нужно удовлетворить дополнительные требования к динамической функциональности.
Динамическое поведение с использованием классических приемов CSS2
В требованиях говорилось, что наше меню навигации по умолчанию будет отображать только значки, но его можно будет расширить для отображения текста (а затем его можно будет свернуть, чтобы снова отображать только значки).
Давайте начнем с того, что просто добавим 5em
к ширине меню навигации, когда оно развернуто. Мы сделаем это, создав «расширенный» класс CSS, который мы можем динамически добавлять или удалять из элемента меню навигации:
#nav { left: 0; width: 5em; &.expanded { /* Sass notation */ width: 10em; } }
Вот пример кода JavaScript (в этом примере мы используем jQuery), который можно использовать для динамического переключения меню навигации между развернутым и свернутым режимами в зависимости от того, щелкает ли пользователь значок переключения навигации:
$('.layout-classic #nav').on('click', 'li.nav-toggle', function() { $('#nav'').toggleClass('expanded'); });
И при этом наше навигационное меню теперь можно динамически разворачивать или сворачивать. Здорово.
Ну вроде здорово, но не совсем. Хотя навигационное меню теперь может расширяться и сжиматься, оно плохо сочетается с остальной частью страницы. Расширенное навигационное меню теперь перекрывает поднавигационное, что определенно нежелательно.
Это раскрывает одно из ключевых ограничений CSS2; а именно , слишком много всего нужно жестко запрограммировать с фиксированными значениями позиции. В результате для других элементов на странице, которые необходимо переместить для размещения расширенного навигационного меню, нам нужно определить дополнительные «расширенные» классы CSS с еще более фиксированными значениями положения.
#subnav { left: 6em; width: 13em; &.expanded { left: 11em; /* move it on over */ } } #main { margin: 6em 0 4em 20; z-index: 10; &.expanded { margin-left: 25em; /* move it on over */ } }
Затем нам нужно расширить наш код JavaScript, чтобы добавить динамическую настройку этих других элементов, когда пользователь щелкает переключатель навигации:
$('.layout-classic #nav').on('click', 'li.nav-toggle', function() { $('#nav, #subnav, #main').toggleClass('expanded'); });
Хорошо, это работает лучше.
Варианты макета с использованием классических методов CSS2
Теперь давайте обратимся к требованию наличия некоторых страниц, которые скрывают подменю навигации. В частности, мы хотим, чтобы подменю навигации было скрыто, когда пользователь щелкает значок «Пользователи» в основной области навигации.
Итак, сначала мы создадим новый класс «скрытый», который применяет display: none
:
.hidden { display: none; }
И снова мы будем использовать JavaScript (jQuery), чтобы применить «скрытый» класс CSS к элементу #subnav
, когда пользователь щелкает значок пользователя:
$('#nav.fa-user').on('click', function() { $('#subnav').toggleClass('hidden'); });
С этим дополнением элемент #subnav
правильно скрывается, когда пользователь щелкает значок «пользователи», но пространство, которое он занимал, остается неиспользованным , а другие элементы расширяются, чтобы использовать пространство, освобожденное элементом #subnav
.
Чтобы получить желаемое поведение, когда мы скрываем элемент #subnav
, мы будем использовать один из менее известных, но очень полезных селекторов CSS, известный как селектор соседних элементов .
Селектор CSS соседнего брата
Селектор соседних элементов позволяет указать два элемента, выбирая только те экземпляры второго элемента, которые следуют непосредственно за указанным первым элементом.
Например, в следующем примере будут выбраны только те элементы с идентификатором main
, которые следуют сразу за элементом с идентификатором subnav
:
#subnav + #main { margin-left: 20em; }
Приведенный выше фрагмент CSS устанавливает левое поле #main
20em
тогда и только тогда , когда оно следует сразу за отображаемым #subnav
.
Однако, если #nav
расширяется (что приводит к тому, что expanded
класс также добавляется к #main
, основываясь на нашем предыдущем коде), мы перемещаем левое поле #main
на 25em.
#subnav + #main.expanded { margin-left: 25em; }
И, если #subnav
скрыт, мы перемещаем левое поле #main
полностью на 6em, чтобы оно было рядом с #nav
:
#subnav.hidden + #main { margin-left: 6em; }
(Примечание. Одним из недостатков использования соседнего родственного селектора является то, что он вынуждает нас всегда иметь #subnav
в DOM, независимо от того, отображается он или нет.)
Наконец, если #subnav
скрыт, а #nav
развернут, мы устанавливаем левое поле #main
на 11em
:
#subnav.hidden + #main.expanded { margin-left: 11em; }
Это позволяет нам соединять вещи вместе без тяжелого кода JavaScript, но мы также можем видеть, насколько сложным может стать этот код, если мы добавим на страницу больше элементов. Мы снова видим, что с CSS2 требуется много жесткого кодирования значений положения, чтобы все работало правильно.
Использование CSS3
CSS3 предлагает значительно улучшенную функциональность и методы компоновки, которые значительно упрощают его использование и гораздо меньше зависят от жестко заданных значений. CSS3 изначально предназначен для поддержки более динамичного поведения и в этом смысле является более «программируемым». Давайте рассмотрим некоторые из этих новых возможностей, поскольку они относятся к нашему варианту использования.
CSS3-функция calc()
Новую функцию CSS3 calc()
можно использовать для динамического вычисления значений свойств CSS (хотя обратите внимание, что поддержка зависит от браузера). Выражение, предоставляемое функции calc()
, может быть любым простым выражением, объединяющим основные арифметические операторы ( +
, -
, *
, /
) с использованием стандартных правил приоритета операций.
Использование функции calc()
может помочь избежать большей части жесткого кодирования значений, требуемых CSS2. В нашем случае это позволяет более динамично расширять CSS. Например:
#nav, #subnav { position: fixed; height: calc(100% - 10em); /* replaces */ z-index: 20; }
С приведенной выше спецификацией height
с использованием функции calc()
мы достигаем того же результата, что и в CSS2 с top:6em
и bottom:4em
, но гораздо более гибким и адаптивным способом, и без необходимости жесткого кодирования top
и bottom
позиции. значения.
Макет CSS3 Flexbox
Flexbox — это новый макет, представленный в CSS3 (поддержка зависит от браузера). Макет flexbox упрощает размещение элементов на странице таким образом, чтобы они вели себя предсказуемо на разных размерах экрана, разрешениях и устройствах. Поэтому он особенно полезен в контексте адаптивного веб-дизайна.
Ключевые особенности включают в себя:
- Позиционирование дочерних элементов стало намного проще, а сложные макеты можно создавать проще и с помощью более чистого кода.
- Дочерние элементы могут располагаться в любом направлении и могут иметь гибкие размеры для адаптации к экранному пространству.
- Дочерние элементы автоматически расширяют контракт, чтобы адаптироваться к доступному свободному пространству.
Flexbox представляет свой собственный уникальный набор терминов и понятий. Вот некоторые из них:
- Флекс-контейнер. Элемент со свойством
display
, установленным наflex
илиinline-flex
, который служит элементом-контейнером для flex-элементов. - Флекс элемент. Любой элемент внутри гибкого контейнера. (Примечание. Текст, непосредственно содержащийся во flex-контейнере, заключен в анонимный flex-элемент.)
- Оси . У каждого макета flexbox есть
flex-directio
, обозначающий главную ось , вдоль которой располагаются flex-элементы. В этом случае поперечная ось является осью, перпендикулярной главной оси. - Линии. Flex-элементы могут располагаться либо на одной, либо на нескольких строках в соответствии со свойством
flex-wrap
. - Габаритные размеры. Эквивалентами высоты и ширины flexbox являются
main size
иcross size
, которые определяют размеры главной оси и поперечной оси flex-контейнера соответственно.
Итак, с этим кратким вступлением, вот альтернативная разметка, которую мы можем использовать, если мы используем макет flexbox:

<body class="layout-flexbox"> <header></header> <div class="content-area"> <nav></nav> <aside></aside> <main></main> </div> <footer></footer> </body>
В нашем примере использования наш основной макет (заголовок, контент, нижний колонтитул) является вертикальным, поэтому мы настроим наш flexbox для использования макета столбца:
.layout-flexbox { display: flex; flex-direction: column; }
Хотя наш основной макет вертикальный, элементы в нашей области контента (навигация, вложенная навигация, основная) расположены горизонтально. Каждый гибкий контейнер может определять только одно направление (т. е. его расположение должно быть либо горизонтальным, либо вертикальным). Поэтому, когда для макета требуется больше, чем это (частый случай для макета приложения), мы можем вложить несколько контейнеров один в другой, каждый с разным направленным макетом.
Вот почему мы добавили дополнительный контейнер (который я назвал content-area
), обертывающий #nav
, #subnav
и #main
. Таким образом, общий макет может быть вертикальным, а содержимое области содержимого может быть расположено горизонтально.
Теперь для позиционирования наших гибких элементов мы будем использовать свойство flex
, которое является сокращением для flex: <flex-grow> <flex-shrink> <flex-basis>;
. Эти три flex-свойства определяют, как наши flex-элементы распределяют любое свободное пространство, оставшееся между ними в направлении потока, следующим образом:
- flex-grow: указывает, насколько элемент может увеличиваться относительно остальных гибких элементов внутри того же контейнера.
- flex-shrink: указывает, как элемент может сжиматься относительно остальных гибких элементов внутри того же контейнера.
- flex-basis: указывает начальный размер элемента (т. е. до того, как он сожмется или вырастет)
Установка flex-grow
и flex-shrink
на ноль означает, что размер элемента фиксирован , и он не будет увеличиваться или уменьшаться, чтобы приспособиться к большему или меньшему количеству свободного места. Это то, что мы делаем для нашего верхнего и нижнего колонтитула, поскольку они имеют фиксированный размер:
#header { flex: 0 0 5em; } #footer { flex: 0 0 3em; }
Чтобы элемент занимал все доступное свободное пространство, установите для его значений flex-grow
и flex-shrink
значение 1, а для flex-basis
значение auto
. Это то, что мы делаем для области контента, так как мы хотим, чтобы она занимала все доступное свободное пространство.
И, как мы говорили ранее, мы хотим, чтобы элементы внутри content-area
располагались в направлении строки, поэтому мы добавим display: flex
; и flex-direction: row;
. Это делает область содержимого новым гибким контейнером для #nav
, #subnav
и `#main.
Итак, вот что у нас получилось с CSS для content-area
:
.content-area { display: flex; flex-direction: row; flex: 1 1 auto; /* take up all available space */ margin: 1em 0; min-height: 0; /* fixes FF issue with minimum height */ }
В области содержимого и #nav
, и #subnav
имеют фиксированные размеры, поэтому мы просто соответствующим образом устанавливаем свойство flex
:
#nav { flex: 0 0 5em; margin-right: 1em; overflow-y: auto; } #subnav { flex: 0 0 13em; overflow-y: auto; margin-right: 1em; }
(Обратите внимание, что я добавил overflow-y: hidden
к этим спецификациям CSS, чтобы предотвратить превышение содержимого и переполнение высоты контейнера. Chrome на самом деле не нуждается в этом, но FireFox в этом нуждается.)
#main
займет оставшееся свободное место:
#main { flex: 1 1 auto; overflow-y: auto; }
Все это выглядит хорошо, так что теперь давайте добавим к этому наше динамическое поведение и посмотрим, что из этого получится.
JavaScript идентичен тому, что у нас было раньше (за исключением того, что класс контейнера элементов CSS, который мы указываем, — это layout-flexbox
тогда как раньше он был layout-classic
):
$('.layout-flexbox #nav').on('click', 'li.nav-toggle', function() { $('#nav').toggleClass('expanded'); });
Мы добавляем expanded
класс в CSS следующим образом:
#nav { flex: 0 0 5em; /* collapsed size */ margin-right: 1em; overflow-y: auto; &.expanded { flex: 0 0 10em; /* expanded size */ } }
И вуаля!
Обратите внимание, что на этот раз нам не нужно сообщать другим элементам об изменении ширины, поскольку макет flexbox обрабатывает все это за нас.
Единственное, что осталось, это скрыть вспомогательную навигацию. И угадайте, что? Это тоже «просто работает», без каких-либо дополнительных изменений, используя тот же код JavaScript, что и раньше. Flexbox знает о свободном пространстве и автоматически заставляет наш макет работать без дополнительного кода. Довольно круто.
Flexbox также предоставляет несколько интересных способов центрирования как вертикальных, так и горизонтальных элементов. Здесь мы понимаем, насколько важно для языка представления включать понятие свободного пространства и насколько масштабируемым может стать наш код, используя такого рода методы. С другой стороны, концепции и обозначения здесь могут потребовать больше времени для освоения, чем в классическом CSS.
Макет сетки CSS3
Если Flexbox Layout находится на переднем крае CSS3, то можно сказать, что Grid Layout находится на переднем крае. Спецификация W3C все еще находится в черновом состоянии и имеет довольно ограниченную поддержку браузеров. (В Chrome он включается с помощью флага «экспериментальные функции веб-платформы» в chrome://flags).
При этом лично я не считаю этот проект революционным. Скорее, как гласят принципы дизайна HTML5: «Когда практика уже широко распространена среди авторов, подумайте о том, чтобы принять ее, а не запрещать ее или изобретать что-то новое».
Соответственно, сетки на основе разметки использовались в течение долгого времени, поэтому теперь CSS Grid Layout просто следует той же парадигме, предлагая все свои преимущества и многое другое на уровне представления без требований к разметке.
Общая идея состоит в том, чтобы иметь предопределенную, фиксированную или гибкую сетку, в которой мы можем размещать наши элементы. Как и flexbox, он также работает по принципу свободного пространства и позволяет нам определять как вертикальные, так и горизонтальные «направления» в одном и том же элементе, что дает преимущества в размере кода и гибкости.
Макет сетки представляет 2 типа сеток; а именно, явное и неявное . Для простоты мы сосредоточимся здесь на явных сетках.
Как и flexbox, макет Grid вводит свой собственный уникальный набор терминов и понятий. Вот некоторые из них:
- Сетчатый контейнер. Элемент со свойством
display
, установленным на «сетку» или «встроенную сетку», в котором содержащиеся элементы размещаются путем позиционирования и выравнивания по предопределенной сетке (явный режим). Сетка представляет собой набор пересекающихся горизонтальных и вертикальных линий сетки, которые делят пространство контейнера сетки на ячейки сетки. Есть два набора линий сетки; один для определения столбцов и один ортогональный ему для определения строк. - Трек сетки. Пространство между двумя соседними линиями сетки. Каждой дорожке сетки назначается функция изменения размера, которая определяет, насколько широким или высоким может быть рост столбца или строки и, таким образом, насколько далеко друг от друга находятся ограничивающие линии сетки.
- Ячейка сетки. Пространство между двумя соседними строками и двумя соседними линиями сетки столбцов. Это наименьшая единица сетки, на которую можно ссылаться при позиционировании элементов сетки.
- Гибкая длина. Измерение, указанное в единице
fr
, которое представляет часть свободного пространства в контейнере сетки.
Вот альтернативная разметка, которую мы можем использовать, если используем макет сетки:
<body class="layout-grid"> <header></header> <nav></nav> <aside></aside> <main></main> <footer></footer> </body>
Обратите внимание, что с этим макетом нам не нужна дополнительная обертка для области содержимого, как мы это делали для flexbox, поскольку этот тип макета позволяет нам определять обозначение пространства элемента в обоих направлениях в одном контейнере сетки.
Давайте теперь углубимся в CSS:
.layout-grid { display: grid; grid-template-columns: auto 0 auto 1em 1fr; grid-template-rows: 5em 1em 1fr 1em 3em; }
Мы определили display: grid;
на нашем контейнере. Свойства grid-template-columns
и grid-template-rows
задаются как список пробелов между дорожками сетки. Другими словами, эти значения не являются положением линий сетки; скорее, они представляют собой расстояние между двумя дорожками.
Обратите внимание, что единицы измерения могут быть указаны как:
- длина
- процент от размера контейнера сетки
- измерение содержимого, занимающего столбец или строку, или
- часть свободного места в сетке
Итак, с grid-template-columns: auto 0 auto 1em 1fr;
Мы будем иметь:
- 1 дорожка, определяющая 2 столбца
auto
ширины (#nav
space) - 1 отступ из 0 (поле для
#subnav
находится на уровне элемента, так как оно может присутствовать или нет, таким образом мы избегаем двойного отступа) - 1 дорожка, определяющая 2 столбца
auto
ширины (#subnav
space) - 1 желоб
1em
- 1 трек с использованием
1fr
для#main
(займет все оставшееся место)
Здесь мы широко используем значение auto
для дорожки, что позволяет нам иметь динамические столбцы, в которых положение и размер строк определяются их максимальным содержанием. (Поэтому нам нужно указать размеры для элементов #nav
и #subnav
, что мы вскоре и сделаем.)
Точно так же для строк строк у нас есть grid-template-rows: 5em 1em 1fr 1em 3em;
который устанавливает #header
и #footer
, а все элементы между ними используют оставшееся свободное пространство при использовании 1em
.
Теперь давайте посмотрим, как мы размещаем фактические элементы для позиционирования в нашей определенной сетке:
#header { grid-column: 1 / 6; grid-row: 1 / 2; } #footer { grid-column: 1 / 6; grid-row: 5 / 6; } #main { grid-column: 5 / 6; grid-row: 3 / 4; overflow-y: auto; }
Это указывает, что мы хотим, чтобы наш заголовок располагался между линиями сетки 1 и 6 (полная ширина) и между линиями сетки 1 и 2 для наших строк. То же самое для нижнего колонтитула, но между двумя последними строками (а не первыми двумя). И основная площадь установлена в соответствии с пространством, которое она должна занимать.
Обратите внимание, что свойства grid-column
и grid-row
являются сокращением для определения grid-column-start
/ grid-column-end
и grid-row-start
/ grid-row-end
соответственно.
Итак, вернемся к #nav
и #subnav
. Поскольку ранее мы размещали #nav
и #subnav
на дорожке с автоматическими значениями, нам нужно указать ширину этих элементов (то же самое для расширенного режима, мы просто меняем его ширину, а Grid Layout позаботится обо всем остальном).
#nav { width: 5em; grid-column: 1 / 2; grid-row: 3 / 4; &.expanded { width: 10em; } } #subnav { grid-column: 3 / 4; grid-row: 3 / 4; width: 13em; /* track has gutter of 0, so add margin here */ margin-left: 1em; }
Итак, теперь мы можем переключать нашу #nav
а также скрывать/удалять #subnav
и все работает отлично! Компоновка сетки также позволяет нам использовать псевдонимы для наших линий, поэтому в конечном итоге изменение сетки не приведет к поломке кода, поскольку оно отображается на имя, а не на линию сетки. Определенно с нетерпением жду, когда это будет более широко поддерживаться большим количеством браузеров.
Заключение
Даже с помощью классических методов CSS можно добиться гораздо большего, чем многие веб-разработчики понимают или используют в своих интересах. Тем не менее, многое из этого может быть довольно утомительным и может включать многократное жесткое кодирование значений в таблице стилей.
CSS3 начал предоставлять гораздо более сложные и гибкие методы компоновки, которые значительно легче программировать и которые позволяют избежать большей части утомительных предыдущих спецификаций CSS.
Овладение этими техниками и парадигмами — как для CSS2, так и для CSS3 — необходимо для использования всего, что может предложить CSS, для оптимизации как взаимодействия с пользователем, так и качества вашего кода. Эта статья на самом деле представляет лишь верхушку айсберга всего, что нужно изучить, и всего, что можно сделать с помощью силы и гибкости CSS. Имейте это!