Селекторы
Базовые селекторы
CSS-селекторы нацеливают HTML-элементы для стилизации. Type-селекторы сопоставляют имена элементов (p, div). Class-селекторы (.) сопоставляют атрибуты class и могут переиспользоваться. ID-селекторы (#) сопоставляют один элемент и должны быть уникальными. Универсальный селектор (*) сопоставляет всё. Группируйте селекторы запятыми для совместного использования стилей.
/* Type selector - all <p> elements */
p { color: #333; }
/* Class selector - elements with class="btn" */
.btn { padding: 8px 16px; }
/* ID selector - element with id="header" */
#header { background: #fff; }
/* Universal selector - all elements */
* { box-sizing: border-box; }
/* Grouping selector */
h1, h2, h3 { font-family: Arial, sans-serif; }Комбинаторы
Комбинаторы определяют отношения между элементами. Descendant (пробел) сопоставляет любой вложенный элемент. Child (>) сопоставляет только прямых потомков. Adjacent sibling (+) сопоставляет элемент сразу после. General sibling (~) сопоставляет всех siblings после. Понимание комбинаторов необходимо для точной стилизации без лишних классов.
/* Descendant - any <a> inside .nav */
.nav a { text-decoration: none; }
/* Child - direct <li> children of <ul> */
ul > li { list-style: none; }
/* Adjacent sibling - <p> right after <h1> */
h1 + p { font-size: 1.2em; }
/* General sibling - all <p> after <h1> */
h1 ~ p { color: gray; }Атрибутные селекторы
Атрибутные селекторы сопоставляют элементы по значениям атрибутов. [attr] проверяет наличие. [attr=val] точное значение. ^= префикс, $= суффикс, *= подстрока. Мощно для стилизации инпутов, ссылок по типу или элементов с data-атрибутами без добавления лишних классов.
/* Elements with type attribute */
[type] { border: 1px solid #ccc; }
/* Exact match */
[type="text"] { padding: 4px; }
/* Starts with */
a[href^="https"] { color: green; }
/* Ends with */
a[href$=".pdf"] { color: red; }
/* Contains */
a[href*="example"] { font-weight: bold; }Псевдоклассы
Псевдоклассы выбирают элементы по состоянию или позиции. Интерактивные: :hover, :focus, :active, :visited, :disabled. Структурные: :first-child, :last-child, :nth-child(n), :nth-of-type(n). :not() отрицает селектор. :nth-child(odd/even) создаёт зебра-полосы. :nth-child(3n) выбирает каждый 3-й элемент. Снижают потребность в лишних классах.
/* Interactive states */
a:hover { color: red; }
a:visited { color: purple; }
input:focus { border-color: blue; }
input:disabled { background: #eee; }
button:active { transform: scale(0.98); }
/* Structural */
li:first-child { font-weight: bold; }
li:last-child { border: none; }
li:nth-child(odd) { background: #f9f9f9; }
li:nth-child(3n) { color: blue; }
p:not(.highlight) { color: #333; }Псевдоэлементы
Псевдоэлементы (::double-colon) стилизуют конкретные части элементов. ::before и ::after вставляют сгенерированный контент (требуется свойство content). ::first-letter и ::first-line стилизуют части текста. ::selection стилизует выделенный текст. ::placeholder стилизует placeholder'ы инпутов. Примечание: ::before/::after по умолчанию inline — установите display:block для блочного поведения.
/* First line of a paragraph */
p::first-line { font-weight: bold; }
/* First letter (drop cap) */
p::first-letter { font-size: 3em; float: left; }
/* Insert content before/after */
.quote::before { content: "\201C"; }
.quote::after { content: "\201D"; }
/* Selection styling */
::selection { background: yellow; color: black; }
/* Placeholder styling */
input::placeholder { color: #999; }Специфичность и !important
Специфичность определяет, какое правило применяется при конфликте селекторов. Инлайновые стили > ID > классы > типы. При равной специфичности побеждает последнее правило. !important переопределяет всё, но ломает каскад — избегайте. Используйте DevTools для проверки специфичности. Предпочитайте селекторы на основе классов для поддерживаемости. Увеличение специфичности повторением классов (.btn.btn) — хак — избегайте.
/* Specificity hierarchy (low to high):
1. Type selectors (p, div) = 0,0,0,1
2. Class selectors (.btn) = 0,0,1,0
3. ID selectors (#header) = 0,1,0,0
4. Inline styles (style="...") = 1,0,0,0
5. !important = overrides all
*/
/* ID beats class */
#nav .link { color: red; } /* wins */
.nav .link { color: blue; }
/* Avoid !important unless necessary */
.critical { color: red !important; }Блочная модель
Margin и Padding
Каждый элемент — коробка с контентом, padding, border и margin. Padding внутри border (влияет на фон), margin снаружи (прозрачный). Shorthand: 1 значение = все стороны, 2 = верх/низ лево/право, 3 = верх лево/право низ, 4 = верх право низ лево (по часовой). margin: 0 auto центрирует блочные элементы с определённой шириной.
.box {
/* TRBL: top right bottom left */
margin: 10px 20px 10px 20px;
/* Shorthand: top/bottom left/right */
margin: 10px 20px;
/* All sides */
padding: 15px;
/* Individual sides */
margin-top: 0;
padding-left: 24px;
/* Center horizontally (block) */
margin: 0 auto;
}Border и Outline
border добавляет линию вокруг padding, влияя на макет. outline рисует снаружи border, не влияя на макет — полезно для индикаторов фокуса. border-radius скругляет углы (одно значение или по углам: верх-левый верх-правый низ-правый низ-левый). outline-offset добавляет пространство между border и outline. Всегда предоставляйте видимые стили фокуса для доступности.
.box {
/* Shorthand: width style color */
border: 2px solid #333;
/* Individual sides */
border-bottom: 3px dashed red;
border-radius: 8px;
/* Outline (doesn't affect layout) */
outline: 2px solid blue;
outline-offset: 4px;
}
/* Remove default focus, add custom */
button:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(0,123,255,0.5);
}Box Sizing
box-sizing: content-box (по умолчанию) означает, что width/height применяются только к контенту — padding и border добавляются к общему размеру. box-sizing: border-box включает padding и border в width/height, делая размер предсказуемым. Всегда устанавливайте border-box глобально для согласованных макетов. Это один из важнейших CSS-reset'ов.
/* Default: width = content only */
.content-box {
box-sizing: content-box;
width: 200px;
padding: 20px;
border: 5px solid;
/* Total width: 200 + 40 + 10 = 250px */
}
/* Recommended: width includes padding+border */
.border-box {
box-sizing: border-box;
width: 200px;
padding: 20px;
border: 5px solid;
/* Total width: 200px */
}
/* Apply globally */
*, *::before, *::after {
box-sizing: border-box;
}Box Shadow
box-shadow добавляет эффекты тени: offset-x offset-y blur-radius spread-radius color. Положительные смещения двигают тень вправо/вниз. inset создаёт внутреннюю тень. Несколько теней наслаиваются (первая = сверху). Используйте rgba для полупрозрачных теней, которые смешиваются естественно. Тени хороши для глубины, но избыток вредит производительности — предпочитайте тонкие тени.
/* offset-x offset-y blur spread color */
.card {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
/* Multiple shadows */
.button {
box-shadow:
0 1px 2px rgba(0,0,0,0.1),
0 4px 8px rgba(0,0,0,0.05);
}
/* Inset shadow */
.input:focus {
box-shadow: inset 0 1px 3px rgba(0,0,0,0.1);
}
/* No shadow */
.no-shadow { box-shadow: none; }Display и Visibility
display: none удаляет элемент из макета полностью (без места). visibility: hidden скрывает, но сохраняет место. opacity: 0 делает прозрачным, но всё ещё интерактивным. block-элементы занимают полную ширину и переносят строку. inline-элементы текут с текстом. inline-block объединяет inline-поток с блочными размерами (width, height). Используйте display: none для переключения контента.
/* Display types */
div { display: block; } /* full width, line break */
span { display: inline; } /* content width, no break */
.inline-block { display: inline-block; } /* inline but block props */
.flex { display: flex; } /* flex container */
.grid { display: grid; } /* grid container */
.none { display: none; } /* removed from layout */
/* Visibility (keeps space) */
.hidden { visibility: hidden; }
/* Opacity (keeps space, can interact) */
.fade { opacity: 0; }Цвета и единицы
Форматы цвета
CSS поддерживает несколько форматов цвета. Hex (#rrggbb) наиболее распространён. RGB/RGBA добавляет alpha-прозрачность. HSL (Hue 0-360, Saturation%, Lightness%) интуитивен — меняйте hue для сдвига цвета, lightness для оттенков. currentColor наследует свойство color элемента. Современный CSS также поддерживает oklch() и color() для более широких gamut'ов. Используйте rgba/hsla для прозрачности.
/* Named colors */
.color { color: red; background: tomato; }
/* Hexadecimal */
.hex { color: #ff6600; } /* 6-digit */
.hex-short { color: #f60; } /* 3-digit shorthand */
/* RGB / RGBA */
.rgb { color: rgb(255, 102, 0); }
.rgba { background: rgba(0, 0, 0, 0.5); }
/* HSL / HSLA (hue, saturation, lightness) */
.hsl { color: hsl(20, 100%, 50%); }
.hsla { background: hsla(120, 100%, 50%, 0.3); }
/* Current color keyword */
.box { color: blue; border: 2px solid currentColor; }Единицы длины
px абсолютный и предсказуемый. em относителен к font-size родителя (компаундируется при вложении). rem относителен к font-size корня (html) — консистентный и предпочтительный для доступности. % относителен к родителю. vh/vw относительны к viewport (100vh = полная высота). Используйте rem для размеров шрифтов, % для макетов, px для границ/тонких деталей.
/* Absolute units */
.px { font-size: 16px; } /* 1px = 1/96 inch */
.pt { font-size: 12pt; } /* 1pt = 1/72 inch */
/* Relative to parent font-size */
.em { font-size: 1.5em; } /* 1.5x parent */
/* Relative to root font-size (html) */
.rem { font-size: 1.2rem; } /* 1.2x root (usually 16px) */
/* Relative to parent dimensions */
.pct { width: 50%; } /* 50% of parent */
/* Viewport units */
.vh { height: 100vh; } /* 100% of viewport height */
.vw { width: 50vw; } /* 50% of viewport width */
.vmin { font-size: 2vmin; } /* 2% of smaller dimension */CSS-функции
calc() выполняет математику со смешанными единицами (например, 100% - 250px). min() выбирает наименьшее значение (отлично для ад аптивных max-width). max() выбирает наибольшее. clamp(min, preferred, max) создаёт текучие значения, масштабирующиеся в границах — идеально для адаптивной типографики. var() ссылается на custom properties с опциональным fallback. Эти функции делают CSS динамичным без media query.
/* calc() - mathematical calculations */
.sidebar { width: calc(100% - 250px); }
.gap { margin: calc(1rem + 10px); }
/* min() / max() - choose smaller/larger */
.responsive { width: min(90%, 1200px); }
.min-width { width: max(300px, 50%); }
/* clamp() - fluid sizing with bounds */
.title { font-size: clamp(1.5rem, 4vw, 3rem); }
/* var() - use custom properties */
.btn { color: var(--primary, #007bff); }Градиенты
Градиенты создают плавные цветовые переходы. linear-gradient(direction, color1, color2) идёт в направлении (to right, 45deg). radial-gradient расширяется из центральной точки. conic-gradient вращается вокруг центра. Можно добавить color stop с позициями: linear-gradient(to right, red 0%, blue 50%, green 100%). Градиенты — изображения, а не цвета, и могут использо ваться везде, где принимается background-image.
/* Linear gradient */
.bg1 {
background: linear-gradient(to right, #ff0000, #0000ff);
}
/* Diagonal with angle */
.bg2 {
background: linear-gradient(45deg, #ff0000, #00ff00, #0000ff);
}
/* Radial gradient */
.bg3 {
background: radial-gradient(circle, #ff0000, #0000ff);
}
/* Conic gradient */
.bg4 {
background: conic-gradient(red, yellow, green, blue, red);
}Фильтры и blend-режимы
filter применяет визуальные эффекты: blur(), brightness(), contrast(), grayscale(), sepia(), hue-rotate(), invert(), opacity(), saturate(). Несколько фильтров цепочкой. mix-blend-mode смешивает элемент с тем, что позади (multiply, screen, overlay и т.д.). backdrop-filter применяет фильтры к области позади элемента (отлично для glassmorphism). Фильтры могут влиять на производительность — используйте умеренно.
/* Filters */
img.bw { filter: grayscale(100%); }
img.blur { filter: blur(5px); }
img.bright { filter: brightness(1.5); }
img.sepia { filter: sepia(0.8); }
/* Multiple filters */
img.vintage {
filter: sepia(0.5) contrast(1.2) brightness(0.9);
}
/* Blend mode */
.overlay {
background: rgba(255,0,0,0.5);
mix-blend-mode: multiply;
}Типографика
Свойства шрифта
font-family принимает fallback-список — браузер использует первый доступный. Всегда заканчивайте generic-семейством (serif, sans-serif, monospace). font-weight от 100 (thin) до 900 (black). line-height: 1.5 означает 1.5x размер шрифта — 1.4-1.6 идеально для читаемости основного текста. Shorthand font должен включать минимум size и family.
body {
/* Font shorthand: style weight size/line-height family */
font: italic bold 16px/1.5 Arial, sans-serif;
/* Individual properties */
font-family: "Helvetica Neue", Arial, sans-serif;
font-size: 16px;
font-weight: 400; /* 100-900, normal=400, bold=700 */
font-style: italic; /* normal, italic, oblique */
line-height: 1.5;
}Стилизация текста
text-align контролирует горизонтальное выравнивание. text-decoration добавляет линии (underline, overline, line-through). text-transform изменяет регистр. letter-spacing (трекинг) и word-spacing регулируют интервалы между символами и словами. white-space: nowrap предотвращает перенос текста. Для вертикального выравнивания используйте vertical-align (inline) или выравнивание flexbox/grid (block).
p {
text-align: justify; /* left, right, center, justify */
text-decoration: underline; /* none, overline, line-through */
text-transform: capitalize; /* uppercase, lowercase, capitalize */
text-indent: 2em; /* indent first line */
letter-spacing: 0.05em; /* tracking */
word-spacing: 0.1em;
white-space: nowrap; /* prevent wrapping */
}
a {
text-decoration: none;
color: #0066cc;
}Переполнение и перенос текста
Для обрезки текста с многоточием нужны три свойства: white-space: nowrap, overflow: hidden и text-overflow: ellipsis. overflow-wrap: break-word разбивает длинные слова для предотвращения переполнения. word-break: break-all разбивает по любому символу. text-wrap: balance (новее) балансирует длину строк для заголовков. Всегда устанавливайте max-width или width для работы обрезки.
/* Truncate with ellipsis */
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
}
/* Break long words */
.break-word {
overflow-wrap: break-word;
word-break: break-all;
}
/* Balance text wrapping (newer) */
h1 {
text-wrap: balance;
}Веб-шрифты (@font-face)
@font-face загружает пользовательские шрифты. woff2 — современный формат (лучшее сжатие). Предоставьте woff как fallback. font-display: swap показывает fallback-текст немедленно, затем заменяет при загрузке шрифта (предотвращает невидимый текст). Всегда объявляйте fallback-семейство шрифтов. Google Fonts предоставляет хостинг-шрифты через @import или <link>. Предзагружайте критические шрифты для производительности.
@font-face {
font-family: 'MyCustomFont';
src: url('fonts/custom.woff2') format('woff2'),
url('fonts/custom.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
body {
font-family: 'MyCustomFont', Arial, sans-serif;
}
/* Google Fonts import */
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');Стилизация списков и ссылок
list-style: none удаляет стандартные маркеры для пользовательской навигации или стилизованных списков. list-style-type изменяет маркер (disc, circle, square, decimal, none). list-style-position: inside помещает маркеры в область контента. Для ссылок важен порядок LVHA: :link, :visited, :hover, :active. Это обеспечивает правильный каскад состояний ссылок.
/* Reset list styles */
ul {
list-style: none;
padding: 0;
margin: 0;
}
/* Custom bullets */
ul.custom {
list-style-type: square;
list-style-position: inside;
}
/* Image bullets */
ul.image {
list-style-image: url('bullet.png');
}
/* Link states (order matters!) */
a:link { color: blue; }
a:visited { color: purple; }
a:hover { color: red; text-decoration: underline; }
a:active { color: orange; }Макет Flexbox
Flex-контейнер
display: flex создаёт flex-контейнер. justify-content выравнивает элементы по главной оси (горизонтально в row). align-items выравнивает по поперечной оси (вертикально). flex-direction меняет главную ось. flex-wrap позволяет элементам переноситься на новые строки. gap устанавливает интервал между элементами (заменяет margin-хаки). Flexbox идеален для 1D-макетов (строки ИЛИ столбцы).
.container {
display: flex;
/* Main axis alignment */
justify-content: space-between;
/* flex-start, center, flex-end, space-around, space-evenly */
/* Cross axis alignment */
align-items: center;
/* flex-start, center, flex-end, stretch, baseline */
/* Direction */
flex-direction: row;
/* row, row-reverse, column, column-reverse */
/* Wrapping */
flex-wrap: wrap;
/* nowrap, wrap, wrap-reverse */
/* Gap between items */
gap: 16px;
}Flex-элементы
flex-grow контролирует, как элементы делят extra-пространство (0 = не расти, 1 = расти поровну). flex-shrink контролирует, как элементы сжимаются при нехватке места. flex-basis устанавливает начальный размер. Shorthand flex: 1 означает flex-grow:1, flex-shrink:1, flex-basis:0%. align-self переопределяет align-items контейнера для одного элемента. order переупорядочивает элементы визуально без изменения DOM.
.item {
/* Grow: how much space to take (0 = don't grow) */
flex-grow: 1;
/* Shrink: how much to shrink when space is tight */
flex-shrink: 0;
/* Basis: initial size before growing/shrinking */
flex-basis: 200px;
/* Shorthand: flex: grow shrink basis */
flex: 1 0 200px;
/* Override align-items for this item */
align-self: flex-end;
/* Order (default 0, lower = earlier) */
order: -1;
}Распространённые flexbox-шаблоны
Flexbox делает центрирование тривиальным: justify-content: center + align-items: center. Для sticky-footer сделайте body flex-column с min-height: 100vh и дайте основному контенту flex: 1. Для navbar justify-content: space-between прижимает первый и последний элементы к противоположным концам. Эти шаблоны решают распространённые проблемы макета, которые были сложными до flexbox.
/* Center anything */
.center {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
/* Sticky footer */
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.main { flex: 1; }
/* Navbar: logo left, links right */
.nav {
display: flex;
justify-content: space-between;
align-items: center;
}Flex Direction и Wrap
flex-direction: column создаёт вертикальные макеты (полезно для стекинга). flex-wrap: wrap позволяет элементам переноситься на новые строки при нехватке места. flex: 1 1 300px означает, что элементы начинаются с 300px, растут для заполнения и сжимаются по необходимости. align-content контролирует интервал между перенесёнными строками (работает только с wrap). Этот шаблон создаёт адаптивные сетки карточек.
/* Horizontal (default) */
.row { display: flex; flex-direction: row; }
/* Vertical */
.column { display: flex; flex-direction: column; }
/* Reverse */
.row-rev { display: flex; flex-direction: row-reverse; }
/* Wrap to new lines */
.cards {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.card { flex: 1 1 300px; } /* grow, shrink, basis */
/* Align wrapped lines */
.cards { align-content: space-between; }Углублённое изучение выравнивания Flexbox
В flex-direction: column главная ось вертикальная, а поперечная — горизонтальная — justify-content и align-items меняются ролями. margin: auto на flex-элементе поглощает всё доступное пространство, идеально центрируя его. Это альтернатива justify-content/align-items. Понимание, какая ось 'главная', а какая 'поперечная' — ключ к освоению выравнивания flexbox.
.container {
display: flex;
flex-direction: column;
/* Main axis = vertical (column) */
justify-content: flex-start;
/* push items to top, center, bottom, or spread */
/* Cross axis = horizontal (column) */
align-items: stretch;
/* stretch items to full width */
}
/* Center a single item perfectly */
.wrapper {
display: flex;
min-height: 100vh;
}
.centered {
margin: auto; /* centers in both axes */
}Макет CSS Grid
Контейнер Grid
display: grid создаёт двумерный макет. grid-template-columns определяет размеры колонок: единицы fr распределяют свободное пространство пропорционально. repeat(3, 1fr) создаёт 3 равные колонки. repeat(auto-fit, minmax(250px, 1fr)) создаёт адаптивную сетку, которая автоматически регулирует количество колонок — это «священный Грааль» адаптивных сеток. gap заменяет margins для интервалов.
.grid {
display: grid;
/* Define columns */
grid-template-columns: 200px 1fr 200px;
/* fixed, flexible, fixed */
/* Or repeat */
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
/* Define rows */
grid-template-rows: auto 1fr auto;
/* Gap (formerly grid-gap) */
gap: 20px;
row-gap: 10px;
column-gap: 20px;
}Размещение элементов Grid
Элементы Grid могут охватывать несколько ячеек с помощью grid-column: span N. Также можно использовать номера линий: grid-column: 1 / 3 означает начало на линии 1, конец на линии 3. Линии нумеруются от 1 (слева/сверху) до N+1 (справа/снизу). grid-area назначает элементы именованным областям. Grid позволяет точное двумерное размещение — элементы могут охватывать одновременно и строки, и колонки.
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}
/* Span multiple columns */
.featured {
grid-column: span 2;
}
/* Explicit placement */
.sidebar {
grid-column: 1 / 3; /* start at line 1, end at line 3 */
grid-row: 1 / 4;
}
/* Named areas */
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }Области шаблона Grid
grid-template-areas создаёт визуальную карту макета с использованием именованных строк. Каждая строка представляет ряд; каждое имя — колонку. Элементы размещаются установкой grid-area в соответствующее имя. Это самый читаемый способ создавать сложные макеты. Используйте '.' для пустых ячеек. Одно имя может охватывать несколько ячеек. Меняйте области в media queries для адаптивных макетов.
.layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 10px;
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }Выравнивание Grid
У Grid 6 свойств выравнивания. align-items/justify-items выравнивают элементы внутри их ячеек grid. align-content/justify-content выравнивают весь трек grid внутри контейнера (видно только если grid меньше). align-self/justify-self переопределяют для отдельного элемента. place-items: center — сокращение для align-items + justify-items. place-content: center объединяет оба выравнивания контента.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
/* Align items within their cell (cross axis) */
align-items: center; /* start, center, end, stretch */
/* Justify items within their cell (main axis) */
justify-items: center;
/* Align the entire grid (if smaller than container) */
align-content: center;
justify-content: center;
}
/* Override for individual item */
.item {
align-self: end;
justify-self: start;
}Адаптивный Grid (auto-fit)
repeat(auto-fit, minmax(300px, 1fr)) создаёт сетку, которая автоматически регулирует количество колонок в зависимости от доступного пространства — элементы имеют минимальный размер 300px и растут для заполнения. Это устраняет необходимость в media queries для карточных макетов. auto-fill сохраняет пустые колонки (элементы остаются слева), auto-fit сворачивает их (элементы растягиваются). Используйте auto-fit для большинства случаев. Комбинируйте с max-width контейнера для оптимальных результатов.
/* Auto-responsive grid - no media queries needed! */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
/* Fixed number of columns per breakpoint */
@media (min-width: 768px) {
.cards {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.cards {
grid-template-columns: repeat(3, 1fr);
}
}
/* Auto-fill vs auto-fit:
auto-fit collapses empty tracks
auto-fill keeps empty tracks */Позиционирование
Типы позиционирования
static — значение по умолчанию (нормальный поток документа). relative смещает относительно нормальной позиции, не затрагивая другие элементы (создаёт контекст позиционирования). absolute удаляет из потока и позиционирует относительно ближайшего позиционированного предка. fixed позиционирует относительно viewport (остаётся при прокрутке). sticky переключается между relative и fixed — прокручивается нормально, затем «прилипает» на пороге.
/* Static (default) - normal flow */
.static { position: static; }
/* Relative - offset from normal position */
.relative {
position: relative;
top: 10px;
left: 20px;
}
/* Absolute - positioned relative to nearest positioned ancestor */
.absolute {
position: absolute;
top: 0;
right: 0;
}
/* Fixed - positioned relative to viewport */
.fixed {
position: fixed;
bottom: 0;
width: 100%;
}
/* Sticky - scrolls then sticks */
.sticky {
position: sticky;
top: 0;
}Абсолютное позиционирование
Абсолютное позиционирование требует позиционированного предка (relative, absolute, fixed или sticky). Без него оно позиционируется относительно viewport. Свойства top/right/bottom/left задают смещение от соответствующего края. Чтобы отцентровать абсолютно позиционированный элемент, используйте top:50%, left:50% с transform: translate(-50%,-50%). Абсолютное позиционирование удаляет элементы из нормального потока.
/* Parent must be positioned */
.container {
position: relative;
width: 400px;
height: 300px;
}
/* Position relative to container */
.badge {
position: absolute;
top: 10px;
right: 10px;
}
/* Center absolutely positioned element */
.modal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}Липкое позиционирование
position: sticky — гибрид: элементы прокручиваются нормально, пока не достигнут порога (например, top: 0), затем прилипают. Отлично подходит для липких заголовков, боковых панелей и заголовков таблиц. Элемент прилипает внутри родительского контейнера (перестаёт прилипать, когда родитель прокручивается мимо). Sticky не работает, если у любого предка есть overflow: hidden/auto/scroll. Всегда устанавливайте z-index для предотвращения проблем с наложением.
/* Sticky header */
.header {
position: sticky;
top: 0;
background: white;
z-index: 100;
padding: 1rem;
}
/* Sticky sidebar */
.sidebar {
position: sticky;
top: 80px; /* offset for fixed header */
height: calc(100vh - 80px);
overflow-y: auto;
}
/* Note: sticky doesn't work if a parent has
overflow: hidden or overflow: auto */Z-index и контекст наложения
z-index управляет порядком наложения позиционированных элементов (больше = поверх). Он работает только на позиционированных элементах (не static). Контекст наложения создаётся позиционированными элементами с z-index, opacity < 1, transform или filter. Внутри контекста наложения дочерние значения z-index относительны этому контексту — дочерний элемент никогда не может оказаться выше сиблинга своего родителя с большим z-index. Это частый источник путаницы.
/* Higher z-index = closer to viewer */
.modal-overlay {
position: fixed;
z-index: 9999;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.5);
}
.modal {
position: fixed;
z-index: 10000; /* above overlay */
top: 50%; left: 50%;
transform: translate(-50%, -50%);
}
/* Stacking context: z-index only works
within the same stacking context */
.parent { position: relative; z-index: 1; }
.child { position: absolute; z-index: 9999; }
/* child can't exceed parent's stacking level */Float и Clear (устаревшее)
float был основным методом вёрстки до flexbox/grid. Он удаляет элементы из нормального потока и сдвигает их влево/вправо, текст обтекает их. clear предотвращает появление элементов рядом с floats. Хак clearfix заставляет контейнер охватывать плавающие дочерние элементы. Сегодня используйте float только по его прямому назначению: обтекание текстом изображений. Для макетов используйте flexbox или grid.
/* Float text around an image */
img.float-left {
float: left;
margin: 0 1rem 1rem 0;
}
/* Clear floats */
.clear { clear: both; }
.clear-left { clear: left; }
.clear-right { clear: right; }
/* Clearfix hack for containers */
.clearfix::after {
content: "";
display: table;
clear: both;
}
/* Modern alternative: use flexbox or grid */
/* Float is mainly for text wrapping now */Адаптивный дизайн
Media queries
Media queries применяют стили на основе условий. min-width (mobile-first) предпочтителен — базовые стили нацелены на мобильные, затем улучшаются для больших экранов. max-width (desktop-first) — наоборот. Другие условия: prefers-color-scheme (тёмный/светлый), print, orientation (portrait/landscape), prefers-reduced-motion. Всегда устанавливайте viewport meta-тег в HTML для работы media queries на мобильных.
/* Mobile-first: base styles first, then min-width */
/* Base (mobile) */
.container { flex-direction: column; }
/* Tablet and up */
@media (min-width: 768px) {
.container { flex-direction: row; }
}
/* Desktop and up */
@media (min-width: 1024px) {
.container { max-width: 1200px; margin: 0 auto; }
}
/* Print styles */
@media print {
.no-print { display: none; }
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
body { background: #1a1a1a; color: #eee; }
}Жидкая типографика
clamp(min, preferred, max) создаёт жидкую типографику, плавно масштабирующуюся с размером viewport — без media queries. Предпочтительное значение (часто на основе vw) масштабируется между границами min и max. min() выбирает меньшее значение (отлично для адаптивных max-width). Этот подход сокращает количество необходимых media queries и создаёт более плавное масштабирование. Всегда устанавливайте разумные min/max для читаемости.
/* clamp() for fluid font sizes */
h1 {
font-size: clamp(1.5rem, 5vw, 3rem);
/* min: 1.5rem, preferred: 5vw, max: 3rem */
}
/* Fluid spacing */
.container {
padding: clamp(1rem, 3vw, 3rem);
}
/* Fluid width */
.content {
width: min(90%, 1200px);
margin: 0 auto;
}
/* Viewport-based sizing */
.hero {
height: 100vh;
font-size: clamp(2rem, 8vw, 5rem);
}Container queries
Container queries (CSS Containment Module Level 3) позволяют стилизовать элементы на основе размера родительского контейнера, а не viewport. container-type: inline-size объявляет контейнер. @container применяет стили, когда контейнер удовлетворяет условию. Это более модульно, чем media queries — компоненты адаптируются к своему контейнеру, а не к экрану. Отлично для переиспользуемых компонентов в разных контекстах вёрстки.
/* Define a container */
.card-container {
container-type: inline-size;
/* or: container: sidebar / inline-size; */
}
/* Query the container's size */
@container (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
}
}
@container (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
}Адаптивные изображения и медиа
max-width: 100% + height: auto делают изображения адаптивными (уменьшаются, но никогда не увеличиваются). Хак с padding-bottom создаёт адаптивные 16:9 видео-вставки. Современное свойство aspect-ratio чище — задайте соотношение, и браузер вычислит высоту. Всегда устанавливайте атрибуты width и height для изображений, чтобы предотвратить сдвиг макета (CLS). Используйте object-fit: cover для обрезки изображений в фиксированных размерах.
/* Responsive image */
img {
max-width: 100%;
height: auto;
}
/* Responsive video (16:9 aspect ratio) */
.video-wrapper {
position: relative;
padding-bottom: 56.25%; /* 9/16 */
height: 0;
}
.video-wrapper iframe {
position: absolute;
top: 0; left: 0;
width: 100%;
height: 100%;
}
/* Aspect ratio property */
.box {
aspect-ratio: 16 / 9;
width: 100%;
}Стратегия Mobile-first
Mobile-first означает написание мобильных стилей как базовых, затем прогрессивное улучшение для больших экранов с media queries min-width. Это гарантирует, что мобильные пользователи (часто на медленных соединениях) загружают меньше CSS. Частые брейкпоинты: 768px (планшет), 1024px (десктоп), 1440px (большой). Используйте относительные единицы (rem, %, vw) вместо фиксированных px для лучшей масштабируемости. Тестируйте на реальных устройствах, а не только в DevTools браузера.
/* 1. Start with mobile base styles */
.nav { display: none; } /* hidden on mobile */
.menu-toggle { display: block; }
/* 2. Enhance for larger screens */
@media (min-width: 768px) {
.nav { display: flex; }
.menu-toggle { display: none; }
}
/* 3. Common breakpoints */
/* Mobile: < 768px */
/* Tablet: 768px - 1023px */
/* Desktop: 1024px - 1439px */
/* Large: >= 1440px */
/* 4. Use relative units */
body { font-size: 1rem; } /* not 16px */Переходы и анимации
Переходы
transition создаёт плавные изменения между значениями свойств. Синтаксис: transition: property duration timing-function delay. Функции тайминга: ease (по умолчанию), linear, ease-in, ease-out, ease-in-out, cubic-bezier(). Переходят только анимируемые свойства. Избегайте transition: all (производительность). Переходы срабатывают при изменениях псевдоклассов (:hover, :focus) или классов через JavaScript.
.button {
background: #007bff;
transition: background 0.3s ease, transform 0.2s ease;
}
.button:hover {
background: #0056b3;
transform: translateY(-2px);
}
/* Transition all properties (use sparingly) */
.card {
transition: all 0.3s ease;
}
/* Shorthand: property duration timing-function delay */
.box {
transition: opacity 0.5s ease-in 0.2s;
}Transform
transform изменяет элемент, не затрагивая макет (в отличие от margin/position). translate сдвигает (translateX, translateY или translate(x,y)). scale изменяет размер (1 = 100%). rotate поворачивает (deg, rad, turn). skew искажает. Несколько трансформаций объединяются по порядку. Трансформации ускоряются GPU — отлично для производительности. Всегда комбинируйте с transition для плавных эффектов. transform-origin меняет точку вращения.
.box {
transition: transform 0.3s ease;
}
/* Translate (move) */
.box:hover { transform: translate(10px, 20px); }
.box:hover { transform: translateX(50%); }
/* Scale (resize) */
.box:hover { transform: scale(1.1); }
/* Rotate */
.box:hover { transform: rotate(45deg); }
/* Skew */
.box:hover { transform: skew(10deg, 5deg); }
/* Combine transforms */
.box:hover {
transform: translate(10px, 0) scale(1.1) rotate(5deg);
}Ключевые кадры анимации
@keyframes определяет шаги анимации. 0%/from — начало, 100%/to — конец. Можно добавить промежуточные шаги (25%, 50%, 75%). Сокращение animation: name duration timing-function delay iteration-count direction fill-mode. infinite зацикливает навсегда. direction: alternate реверсирует на чётных итерациях. fill-mode: forwards сохраняет конечное состояние. Анимации запускаются автоматически, в отличие от переходов.
@keyframes bounce {
0% { transform: translateY(0); }
50% { transform: translateY(-20px); }
100% { transform: translateY(0); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.ball {
animation: bounce 0.5s ease infinite;
}
.element {
animation: fadeIn 1s ease forwards;
/* name duration timing delay iteration direction fill-mode */
}Свойства анимации
Индивидуальные свойства анимации дают точный контроль. animation-iteration-count может быть числом или infinite. animation-direction: alternate проигрывает вперёд, затем назад (отлично для пинг-понг-эффектов). animation-fill-mode: forwards сохраняет конечное состояние после завершения. animation-play-state: paused замораживает анимацию — полезно для паузы при наведении. Несколько анимаций разделяются запятыми: animation: spin 1s, fade 2s;.
.spinner {
animation-name: spin;
animation-duration: 1s;
animation-timing-function: linear;
animation-delay: 0s;
animation-iteration-count: infinite;
animation-direction: normal; /* alternate, reverse */
animation-fill-mode: none; /* forwards, backwards, both */
animation-play-state: running; /* paused */
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Pause on hover */
.spinner:hover {
animation-play-state: paused;
}Производительность и уменьшенное движение
Для плавных анимаций 60fps анимируйте только transform и opacity — они ускоряются GPU и не запускают перерасчёт макета (reflow). Анимация width, margin, top и т. д. заставляет браузер пересчитывать макет для каждого кадра, вызывая рывки. Всегда уважайте prefers-reduced-motion: reduce — некоторые пользователи испытывают укачивание или имеют вестибулярные расстройства. Предоставляйте мгновенные переходы или отключайте анимации для этих пользователей.
/* Only animate transform and opacity for performance */
.good {
transition: transform 0.3s, opacity 0.3s;
}
/* Avoid animating these (cause reflow/repaint): */
/* margin, padding, width, height, top, left */
/* Respect user's motion preference */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}Переменные и продвинутые функции
Пользовательские свойства (переменные)
Пользовательские свойства (CSS-переменные) хранят переиспользуемые значения. Определяйте их в :root для глобального доступа. Ссылайтесь через var(--name). Они каскадируются и могут быть переопределены в любой области — идеально для темизации (тёмный/светлый режим). В отличие от переменных препроцессора, они динамические (изменяются во время выполнения) и могут управляться через JavaScript. Всегда предоставляйте фолбэки: var(--primary, #007bff). Это современный способ управления дизайн-токенами.
:root {
/* Colors */
--primary: #007bff;
--primary-dark: #0056b3;
--text: #333;
--bg: #fff;
/* Spacing scale */
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 2rem;
/* Font sizes */
--font-base: 16px;
--font-lg: 1.5rem;
}
.button {
background: var(--primary);
padding: var(--space-sm) var(--space-md);
font-size: var(--font-base);
color: white;
}
/* Override in a scope */
.dark-theme {
--bg: #1a1a1a;
--text: #eee;
}calc() и математические функции
calc() выполняет математические операции с любыми единицами, включая смешивание px, %, em, rem, vw. Это необходимо для адаптивных макетов. Можно вкладывать calc(), но это не нужно — calc(100% - 2rem) / 3 работает без внутреннего calc. min() возвращает наименьшее значение, max() — наибольшее, clamp() ограничивает значение между min и max. Эти функции делают CSS динамичным без JavaScript или media queries.
/* Mixed unit calculations */
.sidebar {
width: calc(100% - 250px);
}
/* With variables */
.box {
padding: calc(var(--space-md) + 10px);
}
/* Nested calc */
.responsive {
width: calc(calc(100% - 2rem) / 3);
}
/* min(), max(), clamp() */
.fluid {
width: min(90%, 1200px);
font-size: clamp(1rem, 2vw + 1rem, 2rem);
height: max(200px, 50vh);
}Object fit и position
object-fit управляет тем, как изображения/видео заполняют контейнер (как background-size для <img>). cover заполняет контейнер и обрезает переполнение (отлично для аватаров/превью). contain помещается целиком без обрезки (может оставлять пустое пространство). fill растягивает (искажает). object-position регулирует выравнивание (как background-position). Это заменяет хаки с background-image для адаптивных изображений.
/* Cover: fill container, crop overflow */
.avatar {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 50%;
}
/* Contain: fit entirely, may letterbox */
.preview {
width: 200px;
height: 200px;
object-fit: contain;
background: #eee;
}
/* Position the image within the frame */
.image {
object-fit: cover;
object-position: top center;
}Свойства фона
background — мощное сокращение. background-size: cover заполняет контейнер (с обрезкой); contain помещается без обрезки. background-attachment: fixed создаёт эффект параллакса (изображение остаётся неподвижным при прокрутке). Несколько фонов наслаиваются запятыми (первый = верхний слой). Используйте linear-gradient как фон для оверлеев: background: linear-gradient(rgba(0,0,0,0.5), transparent), url('image.jpg').
.hero {
/* Shorthand */
background: url('hero.jpg') center/cover no-repeat fixed;
/* Individual properties */
background-image: url('hero.jpg');
background-size: cover; /* cover, contain, or px/% */
background-position: center; /* top, bottom, left, right, or x y */
background-repeat: no-repeat;
background-attachment: fixed; /* scroll, fixed, local */
/* Multiple backgrounds */
background:
url('overlay.png') no-repeat center,
url('hero.jpg') center/cover;
}Вложенность CSS (современная)
Современный CSS поддерживает нативную вложенность (как Sass/Less). Используйте & для ссылки на родительский селектор. Вложенность улучшает читаемость и сокращает повторения. Media queries можно вкладывать прямо внутрь правил. Поддержка браузерами хорошая в современных браузерах (2023+). & требуется для псевдоклассов (&:hover) и комбинаторов (& > .child). Избегайте глубокой вложенности (3+ уровня), так как она повышает специфичность и снижает поддерживаемость.
/* Native CSS nesting (no preprocessor needed) */
.card {
background: white;
border-radius: 8px;
& .title {
font-size: 1.5rem;
font-weight: bold;
}
& .body {
padding: 1rem;
& p {
line-height: 1.6;
}
}
&:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
@media (max-width: 768px) {
padding: 0.5rem;
}
}Углублённое изучение пользовательских свойств CSS (переменных)
Определение и использование переменных
Пользовательские свойства CSS (переменные) определяются с --name и используются с var(). Определяйте глобальные переменные в :root для тематических значений. В отличие от переменных Sass, CSS-переменные живые — изменение их обновляет все использования мгновенно. Они ограничены областью элемента, на котором определены, и наследуются потомками. var() принимает фолбэк-значение как второй аргумент, и фолбэки могут объединяться в цепочку. Это делает их гораздо более мощными, чем переменные препроцессора, для динамической темизации.
:root {
--primary: #3498db;
--spacing: 16px;
--radius: 8px;
--max-width: 1200px;
}
.button {
background: var(--primary);
padding: var(--spacing);
border-radius: var(--radius);
max-width: var(--max-width);
}
/* Fallback values */
color: var(--text-color, #333);
/* Chained fallbacks */
color: var(--text, var(--default, black));Динамическая темизация с JavaScript
CSS-переменные обеспечивают темизацию во время выполнения, недоступную препроцессорам. Установите data-theme на <html> и переопределяйте переменные для каждой темы. JavaScript может читать (getComputedStyle) и устанавливать (style.setProperty) переменные во время выполнения, что позволяет создавать динамические палитры цветов, пользовательские настройки и живые превью. Это стандартный подход для тёмного режима, настройки бренда и выбираемых пользователем тем. Переменные каскадируются и наследуются, поэтому переопределение в дочернем элементе затрагивает только это поддерево.
/* Define theme variables */
:root {
--bg: #ffffff;
--text: #333333;
}
[data-theme="dark"] {
--bg: #1a1a1a;
--text: #e0e0e0;
}
body {
background: var(--bg);
color: var(--text);
}
/* Toggle theme with JS */
<script>
document.documentElement.setAttribute(
"data-theme",
isDark ? "dark" : "light"
);
/* Or set a single variable directly */
document.documentElement.style
.setProperty("--primary", "#e74c3c");
</script>Область видимости переменных и наследование
CSS-переменные наследуются как другие свойств а. Переменная, определённая на :root, доступна везде; определённая на .card затрагивает только .card и его потомков. Это ограничение области видимости позволяет настраивать компоненты на уровне компонента. Можно переопределять переменные в media queries для создания адаптивных значений переменных — сама переменная не может использоваться в условиях @media, но можно изменить её значение внутри блока media query. Этот паттерн мощен для адаптивного дизайна без повторения объявлений свойств.
:root {
--padding: 20px;
}
.card {
--padding: 12px; /* overrides only for .card subtree */
padding: var(--padding);
}
.card .inner {
padding: var(--padding); /* inherits 12px from .card */
}
/* Variables in media queries don't work, but... */
.sidebar {
--width: 250px;
width: var(--width);
}
@media (max-width: 768px) {
.sidebar { --width: 100%; }
}Переменные с calc()
Сочетание CSS-переменных с calc() создаёт мощные дизайн-системы. Определите базовый размер и коэффициент масштабирования, затем вычислите производные размеры. Это обеспечивает согласованные типографические шкалы и системы интервалов. calc() работает со смешанными единицами (px, %, em, vw) и переменными. Можно даже использовать переменные без фолбэков (var(--nav-h, 60px) предоставляет значение по умолчанию). Этот подход — основа современных дизайн-систем — измените одну переменную, и вся шкала изменяется пропорционально.
:root {
--base-size: 16px;
--scale: 1.25;
}
h1 { font-size: calc(var(--base-size) * var(--scale) * 2); }
h2 { font-size: calc(var(--base-size) * var(--scale) * 1.5); }
p { font-size: var(--base-size); }
/* Spacing scale */
:root {
--space: 8px;
}
.margin { margin: calc(var(--space) * 2); }
.padding { padding: calc(var(--space) * 3); }
/* Mixed units */
.header { height: calc(var(--nav-h, 60px) + var(--content-h)); }Регистрация пользовательских свойств (@property)
@property регистрирует пользовательские свойства с тип ом (syntax), начальным значением и флагом наследования. Это позволяет CSS-переменным анимироваться и переходить — без @property переменные трактуются как строки и не могут интерполироваться. syntax принимает типы вроде <angle>, <color>, <length>, <number>, <percentage> или '*'. Это открывает плавные анимации градиентов, трансформаций и цветов, управляемые переменными. Поддержка браузерами хорошая в Chromium и Safari, Firefox добавляет поддержку. Значительное продвижение возможностей анимации CSS.
/* Type-safe custom properties */
@property --angle {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
@property --color {
syntax: "<color>";
initial-value: #3498db;
inherits: false;
}
/* Now animatable! */
@keyframes spin {
to { --angle: 360deg; }
}
.loader {
--angle: 0deg;
animation: spin 1s linear infinite;
background: conic-gradient(
from var(--angle),
var(--color), transparent
);
}3D-трансформации
Функции 3D-трансформаций
3D-трансформации добавляют элементам глубину. perspective() как функция трансформации применяется к отдельному элементу; свойство perspective на родителе применяется ко всем дочерним элементам равномерно. rotateX/rotateY/rotateZ вращают вокруг осей; translateZ сдвигает вдоль оси Z (к/от зрителя). Меньшие значения perspective создают более драматичные 3D-эффекты (ближняя точка обзора). Всегда устанавливайте perspective на родительский контейнер для согласованного 3D-пространства между несколькими трансформированными дочерними элементами. Комбинируйте несколько трансформаций для сложного 3D-позиционирования.
.card {
transform: perspective(1000px) rotateY(45deg) rotateX(15deg);
}
/* Using the perspective property instead */
.container {
perspective: 1000px;
}
.container .card {
transform: rotateY(45deg) rotateX(15deg);
}
/* translateZ moves toward/away from viewer */
.layer {
transform: translateZ(100px);
}
/* scale3d for 3D scaling */
.box {
transform: scale3d(1.5, 1.5, 1.5);
}transform-style и backface-visibility
transform-style: preserve-3d сохраняет 3D-пространство для дочерних элементов (flat — по умолчанию, что «сплющивает» дочерние элементы). backface-visibility: hidden скрывает заднюю сторону элемента при вращении — необходимо для эффектов flip-карточки. Классическая flip-карточка использует два абсолютно позиционированных лица: переднее смотрит вперёд, заднее предварительно повёрнуто на 180deg. При наведении родитель поворачивается на 180deg, меняя видимое лицо. Это один из самых популярных 3D CSS-паттернов.
.container {
perspective: 1000px;
}
.flip-card {
transform-style: preserve-3d;
transition: transform 0.6s;
position: relative;
}
.flip-card:hover {
transform: rotateY(180deg);
}
.flip-card .front,
.flip-card .back {
position: absolute;
inset: 0;
backface-visibility: hidden;
}
.flip-card .back {
transform: rotateY(180deg);
}3D-куб
3D-куб строится из 6 граней, каждая позиционируется с rotate + translateZ. translateZ(100px) сдвигает грань на половину ширины куба наружу. Каждая грань предварительно поворачивается в нужном направлении перед сдвигом. transform-style: preserve-3d на контейнере куба необходим — без него грани сплющиваются. Анимация вращает куб по обеим осям X и Y. Это демонстрирует полную мощь 3D-трансформаций CSS. Настройте translateZ на половину размера вашего куба для правильной геометрии.
.scene {
perspective: 800px;
width: 200px; height: 200px;
}
.cube {
position: relative;
width: 100%; height: 100%;
transform-style: preserve-3d;
animation: rotate 8s infinite linear;
}
@keyframes rotate {
from { transform: rotateX(0) rotateY(0); }
to { transform: rotateX(360deg) rotateY(360deg); }
}
.face {
position: absolute;
width: 200px; height: 200px;
opacity: 0.85;
}
.front { transform: translateZ(100px); background: red; }
.back { transform: rotateY(180deg) translateZ(100px); background: blue; }
.right { transform: rotateY(90deg) translateZ(100px); background: green; }
.left { transform: rotateY(-90deg) translateZ(100px); background: yellow; }
.top { transform: rotateX(90deg) translateZ(100px); background: purple; }
.bottom { transform: rotateX(-90deg) translateZ(100px); background: orange; }Эффект 3D-наклона карточки
Эффект наклона карточки отслеживает позицию мыши и поворачивает карточку в 3D на основе смещения курсора от центра. Математика делит смещение на коэффициент (10) для ограничения угла вращения. При mouseleave карточка возвращается в плоское состояние. transition создаёт плавное движение. Добавление оверлея блика/свечения с радиальным градиентом, следующего за мышью, усиливает эффект. Это популярный интерактивный UI-паттерн для карточек товаров и hero-секций. perspective в строке transform обеспечивает 3D-рендеринг даже без свойства perspective у родителя.
.tilt-card {
transition: transform 0.1s ease-out;
transform-style: preserve-3d;
}
/* JavaScript-driven tilt */
<script>
const card = document.querySelector(".tilt-card");
card.addEventListener("mousemove", (e) => {
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const rotateX = (y - centerY) / 10;
const rotateY = (centerX - x) / 10;
card.style.transform =
`perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
});
card.addEventListener("mouseleave", () => {
card.style.transform = "perspective(1000px) rotateX(0) rotateY(0)";
});
</script>perspective-origin и 3D-интервалы
perspective-origin устанавливает позицию точки схода (как движение головы). По умолчанию 50% 50% (центр). Перемещение создаёт динамические углы обзора. Для эффектов параллакса элементы с разными значениями translateZ движутся с разной скоростью при прокрутке или вращении контейнера. Элементы дальше назад (отрицательный translateZ) требуют scale() для компенсации перспективного сжатия. Эта техника создаёт глубину в UI без JavaScript — чистый CSS-параллакс. Сочетание со scroll-driven анимациями создаёт иммерсивные впечатления от прокрутки.
.stage {
perspective: 1200px;
perspective-origin: 50% 30%; /* x y position of vanishing point */
transform-style: preserve-3d;
}
.layer-1 { transform: translateZ(0); }
.layer-2 { transform: translateZ(50px); }
.layer-3 { transform: translateZ(100px); }
/* Parallax-like depth */
.depth-bg { transform: translateZ(-200px) scale(1.2); }
.depth-mid { transform: translateZ(-100px) scale(1.1); }
.depth-fg { transform: translateZ(0); }CSS-фильтры и backdrop-фильтры
Функции фильтров
CSS-фильтры применяют визуальные эффекты к элементам. grayscale, sepia и invert изменяют цвета; blur смягчает; brightness/contrast/saturate регулируют интенсивность; hue-rotate сдвигает цвета. Несколько фильтров объединяются слева направо. drop-shadow превосходит box-shadow для PNG/SVG-изображений, так как следует форме альфа-ка нала (прозрачные области не затеняются). Фильтры ускоряются GPU и работают хорошо. Частое использование: grayscale при наведении для галерей изображений, винтажные фотоэффекты и доступность (режимы высокой контрастности).
img.grayscale { filter: grayscale(100%); }
img.blur { filter: blur(5px); }
img.bright { filter: brightness(1.5); }
img.contrast { filter: contrast(200%); }
img.sepia { filter: sepia(80%); }
img.saturate { filter: saturate(2); }
img.hue { filter: hue-rotate(90deg); }
img.invert { filter: invert(100%); }
/* Combine multiple filters */
img.vintage {
filter: sepia(50%) contrast(110%) brightness(90%) saturate(120%);
}
/* Drop shadow (follows alpha shape) */
img.png-shadow {
filter: drop-shadow(2px 4px 6px rgba(0,0,0,0.4));
}backdrop-filter (стекломорфизм)
backdrop-filter применяет фильтры к области позади элемента (не к самому элементу), создавая эффект стеклообразности. Элементу нужен полупрозрачный фон, чтобы эффект был виден. blur — наиболее частый backdrop-фильтр для матового стекла. Всегда включ айте префикс -webkit- для поддержки Safari. Комбинируйте с полупрозрачными границами и лёгкими box-shadows для отполированного стеклянного вида. Возможны проблемы с производительностью при больших размытых областях — используйте умеренно. Это определяющий тренд современного UI-дизайна (iOS, macOS, Windows Acrylic).
.glass {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px) saturate(180%);
-webkit-backdrop-filter: blur(10px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 16px;
padding: 24px;
}
.glass-dark {
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
}SVG-фильтры (ссылка url)
CSS filter: url(#id) ссылается на SVG-фильтры для эффектов, выходящих за рамки встроенных функций. Фильтр 'gooey' создаёт каплеобразное слияние элементов (популярно для loading-анимаций и органичного UI). feGaussianBlur + feColorMatrix с высоким множителем alpha создаёт эффект gooey, о бостряя размытые края. feTurbulence + feDisplacementMap создаёт волнистые/искажённые эффекты. SVG-фильтры мощные, но могут быть требовательны к производительности. Они позволяют эффекты, невозможные только средствами CSS: жидкое морфирование, смещение, освещение и пользовательское смешивание.
<!-- Define SVG filter -->
<svg style="display:none">
<filter id="gooey">
<feGaussianBlur in="SourceGraphic" stdDeviation="10" />
<feColorMatrix values="
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 20 -10" />
</filter>
</svg>
<!-- Apply via CSS -->
.gooey-container {
filter: url(#gooey);
}
<!-- Displacement map for wavy text -->
<filter id="wavy">
<feTurbulence baseFrequency="0.02" numOctaves="3" />
<feDisplacementMap in="SourceGraphic" scale="10" />
</filter>Анимация фильтров
Фильтры можно анимировать и плавно переходить. Эфект pulse-glow сочетает brightness и drop-shadow для неонового све чения. Grayscale-в-цвет при наведении — классическое взаимодействие в галерее. Переходы фильтров ускоряются GPU и работают лучше, чем анимация box-shadow или background-color. Однако анимация blur или сложных фильтров может быть дорогостоящей — тестируйте на мобильных. Scroll-driven hue-rotate — забавный эффект, но может вызывать проблемы с производительностью на длинных страницах; используйте will-change: filter, чтобы подсказать браузеру оптимизацию.
@keyframes pulse-glow {
0%, 100% {
filter: brightness(1) drop-shadow(0 0 5px #3498db);
}
50% {
filter: brightness(1.3) drop-shadow(0 0 20px #3498db);
}
}
.glow-button {
animation: pulse-glow 2s ease-in-out infinite;
}
/* Hover filter transition */
.gallery img {
filter: grayscale(100%);
transition: filter 0.3s ease;
}
.gallery img:hover {
filter: grayscale(0%);
}
/* Color shift on scroll (with JS) */
window.addEventListener("scroll", () => {
const hue = window.scrollY * 0.5;
document.body.style.filter = `hue-rotate(${hue}deg)`;
});mix-blend-mode и background-blend-mode
mix-blend-mode о пределяет, как пиксели элемента смешиваются с контентом позади него. multiply затемняет (хорошо для теней и водяных знаков); screen осветляет (хорошо для свечения); difference создаёт психоделические эффекты; overlay сочетает multiply и screen для контраста. background-blend-mode смешивает несколько слоёв фона (изображения и градиенты) на одном элементе. Режимы смешивания необходимы для творческого композитинга, двухтоновых эффектов и наложений текста на изображение. isolation: isolate создаёт новый контекст смешивания для ограничения эффектов смешивания.
/* mix-blend-mode: how element blends with backdrop */
.overlay {
mix-blend-mode: multiply; /* darken */
}
.overlay-screen {
mix-blend-mode: screen; /* lighten */
}
.overlay-difference {
mix-blend-mode: difference; /* invert */
}
/* Common blend modes: multiply, screen, overlay,
darken, lighten, color-dodge, color-burn,
hard-light, soft-light, difference, exclusion,
hue, saturation, color, luminosity */
/* background-blend-mode: blend background layers */
.gradient-texture {
background-image: url(texture.png),
linear-gradient(45deg, red, blue);
background-blend-mode: multiply;
}Container queries
Основы container queries
Container queries позволяют компонентам реагировать на размер контейнера, а не viewport. container-type: inline-size делает элемент контейнером запросов на основе его inline-размерности (ширины). Правило @container применяет стили, когда контейнер удовлетворяет условию. Это революционно для компонентного дизайна — карточка адаптируется, будь то в боковой панели или полноширинной основной области, независимо от размера экрана. Это решает фундаментальное ограничение media queries, которые реагируют только на размер viewport.
/* Define a container */
.card-container {
container-type: inline-size;
/* or: container-type: size; (both dimensions) */
}
/* Query the container's size */
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
@container (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
}Именованные контейнеры
Именованные контейнеры позволяют целиться в конкретные контейнеры при вложенности нескольких. container-name даёт контейнеру метку; @container name (condition) запрашивает только этот контейнер. Это предотвращает конфликты, когда компоненты вложены с разными контекстами контейнеров. Сокращение container объединяет имя и тип: container: panel / inline-size. Именованные контейнеры необходимы для сложных макетов, где сосуществуют несколько независимых container queries. Без имён @container запрашивает ближайший родительский контейнер.
/* Name a container for specific queries */
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
.main-content {
container-type: inline-size;
container-name: main;
}
/* Query specific container by name */
@container sidebar (min-width: 300px) {
.widget { display: grid; grid-template-columns: 1fr 1fr; }
}
@container main (min-width: 600px) {
.article { columns: 2; }
}
/* Shorthand: container: name / type */
.panel {
container: panel / inline-size;
}Единицы container queries
Единицы container queries (cqw, cqh, cqmin, cqmax) подобны viewport-единицам (vw, vh), но относительны контейнеру запросов, а не viewport. 1cqw = 1% ширины контейнера. Это обеспечивает истинно компонентно-адаптивную типографику и интервалы — текст масштабируется с шириной компонента, а не экрана. Сочетание с clamp() даёт жидкую типографику, адаптирующуюся к размеру контейнера с границами min/max. Идеально для дизайн-систем, где компоненты должны работать в разных контекстах вёрстки. Примечание: cqh/cqmin/cqmax требуют container-type: size (обе размерности).
.card {
container-type: inline-size;
}
.card-title {
/* cqw: 1% of container width */
font-size: 5cqw;
/* cqh: 1% of container height (needs size type) */
padding: 2cqh;
/* cqmin: 1% of smaller container dimension */
margin: 2cqmin;
/* cqmax: 1% of larger container dimension */
border-radius: 1cqmax;
}
/* Fluid typography that responds to container */
.headline {
font-size: clamp(1rem, 8cqw, 3rem);
}Запросы стилей контейнера
Запросы стилей контейнера (экспериментальные, только Chromium на 2025 год) позволяют запрашивать значения пользовательских свойств контейнера, а не только размерности. Это обеспечивает условный рендеринг на основе стилей: компонент может менять внешний вид на основе переменной --theme, заданной на его контейнере. container-type: style (или container-type не требуется для запросов стиля) включает это. Это мощно для темизации — установите переменную на родителе, и все дочерние элементы адаптируются. Полная поддержка браузерами всё ещё ожидается; используйте с прогрессивным улучшением или проверками @supports.
/* Query custom property values on container */
.card-wrapper {
container-type: style;
container-name: card;
}
@container card style(--theme: dark) {
.card {
background: #1a1a1a;
color: #fff;
}
}
@container card style(--theme: light) {
.card {
background: #fff;
color: #333;
}
}
/* Set the theme variable */
<div class="card-wrapper" style="--theme: dark">
<div class="card">...</div>
</div>Паттерн адаптивного компонента
Этот паттерн создаёт истинно переиспользуемый компонент, адаптирующийся к размещению. Карточка товара показывает вертикальный макет в узких контейнерах, горизонтальный в средних и 3-колоночную сетку в широких. Тот же компонент работает в боковой панели (узкой), сетке (средней) или hero-секции (широкой) без какой-либо условной логики на основе props. Это убийственный вариант использования container queries — компоненты, действительно независимые от контекста. Сочетание с единицами container queries для типографики даёт полностью сам адаптирующиеся компоненты.
/* Component that adapts to any container */
.product-card {
container-type: inline-size;
container-name: product;
}
.product-card .layout {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Medium container: horizontal layout */
@container product (min-width: 350px) {
.product-card .layout {
flex-direction: row;
}
.product-card .image {
width: 40%;
}
}
/* Large container: full feature layout */
@container product (min-width: 600px) {
.product-card .layout {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
}
.product-card .actions {
display: flex;
flex-direction: column;
}
}Scroll Snap и scroll-driven анимации
Основы scroll snap
Scroll snap создаёт магнитную прокрутку, выравнивающую элементы по точкам привязки. scroll-snap-type: x/y задаёт ось; mandatory принудительно привязывает (всегда останавливается на точке); proximity привязывает только при близости. scroll-snap-align: start/center/end определяет, где дочерний элемент привязывается внутри контейнера. Полноэкранные прокручиваемые секции (как слайды презентации) используют y mandatory с секциями 100vh. Горизонтальные карусели используют x mandatory. Всегда тестируйте на сенсорных устройствах — mandatory может ощущаться ограничительно, если контент выше viewport. Используйте proximity для более мягкого поведения.
/* Container with scroll snapping */
.gallery {
scroll-snap-type: x mandatory;
overflow-x: auto;
display: flex;
gap: 1rem;
}
/* Children snap to positions */
.gallery section {
scroll-snap-align: start;
flex: 0 0 100%;
height: 300px;
}
/* Vertical snapping */
.full-page {
scroll-snap-type: y mandatory;
height: 100vh;
overflow-y: scroll;
}
.full-page section {
scroll-snap-align: start;
height: 100vh;
}Scroll snap с padding
scroll-padding смещает точки привязки от края контейнера — полезно, когда есть фиксированные заголовки или нужен отступ вокруг привязанных элементов. scroll-snap-stop: always предотвращает быструю прокрутку через несколько элементов (принуждает к поочерёдной привязке). scroll-snap-align: center привязывает элементы к центру контейнера, создавая эффект cover-flow. Эти свойства дают точный контроль над повед ением привязки. Для каруселей изображений комбинируйте с scroll-behavior: smooth для анимированных переходов между привязками.
.carousel {
scroll-snap-type: x mandatory;
scroll-padding: 0 20px; /* offset snap points */
overflow-x: auto;
display: flex;
gap: 1rem;
padding: 0 20px;
}
.carousel .item {
scroll-snap-align: start;
scroll-snap-stop: always; /* don't skip items */
flex: 0 0 80%;
}
/* Center-aligned snapping */
.carousel-center .item {
scroll-snap-align: center;
flex: 0 0 60%;
}Scroll-driven анимации (animation-timeline)
Scroll-driven анимации (только CSS, без JS!) связывают анимации с позицией прокрутки. animation-timeline: scroll() привязывает анимацию к прогрессу прокрутки страницы (0% вверху, 100% внизу). animation-timeline: view() привязывает к появлению/исчезновению элемента из viewport. animation-range определяет, когда анимация начинается/заканчивается относительно прокрутки (entry, exit, cover, contain). Это позволяет полосы прогресса прокрутки, появление при прокрутке и параллакс-эффекты без JavaScript или слушателей событий прокрутки. Поддерживается в Chrome 115+, рекомендуется прогрессивное улучшение для других браузеров.
/* Animate as user scrolls */
@keyframes fade-in {
from { opacity: 0; transform: translateY(50px); }
to { opacity: 1; transform: translateY(0); }
}
.reveal {
animation: fade-in linear;
animation-timeline: view();
animation-range: entry 0% cover 40%;
}
/* Progress bar based on scroll position */
.progress-bar {
position: fixed;
top: 0; left: 0;
height: 4px;
background: #3498db;
width: 100%;
transform-origin: left;
animation: grow linear;
animation-timeline: scroll();
}
@keyframes grow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}Диапазоны view timeline
Диапазоны view timeline точно контролируют, когда возникает анимация. 'entry' — когда элемент входит во viewport; 'exit' — когда покидает; 'cover' — от начала entry до конца exit; 'contain' — когда элемент полностью виден. Проценты точно настраивают в каждом диапазоне. animation-range: entry 10% entry 90% означает, что анимация выполняется от 10% entry до 90% entry. Для липких заголовков scroll() с пиксельным диапазоном (0 200px) анимирует на основе абсолютного расстояния прокрутки. Это заменяет сложные JavaScript-обработчики прокрутки декларативным CSS.
/* Element animates as it enters viewport */
@keyframes slide-up {
from { opacity: 0; transform: translateY(100px); }
to { opacity: 1; transform: translateY(0); }
}
.section {
animation: slide-up linear both;
animation-timeline: view();
/* Ranges: entry, exit, cover, contain */
animation-range: entry 10% entry 90%;
}
/* Sticky header that shrinks on scroll */
.header {
position: sticky;
top: 0;
animation: shrink linear both;
animation-timeline: scroll();
animation-range: 0 200px;
}
@keyframes shrink {
to { padding: 0.5rem 1rem; font-size: 0.9em; }
}scroll-behavior и плавная прокрутка
scroll-behavior: smooth на html (или любом контейнере прокрутки) делает навигацию по якорным ссылкам и scrollIntoView() анимированной плавно, а не резкой. Это однострочная замена JavaScript-библиотекам плавной прокрутки. Всегда уважайте prefers-reduced-motion — некоторые пользователи испытывают укачивание, поэтому отключайте плавную прокрутку для них. scroll-snap-type: proximity (vs mandatory) хорошо сочетается с плавной прокруткой для естественного ощущения. scrollIntoView с block: 'start'/'center'/'end' контролирует вертикальное выравнивание цели.
/* Smooth scrolling for anchor links */
html {
scroll-behavior: smooth;
}
/* With scroll snap */
html {
scroll-behavior: smooth;
scroll-snap-type: y proximity;
}
/* Target specific elements */
.container {
scroll-behavior: smooth;
overflow-y: auto;
}
/* JavaScript: scroll to element */
<script>
document.querySelector(".section-3")
.scrollIntoView({ behavior: "smooth", block: "start" });
/* Scroll to top */
window.scrollTo({ top: 0, behavior: "smooth" });
</script>
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
}CSS-счётчики
Базовый счётчик
CSS-счётчики автоматически нумеруют элементы без JavaScript. counter-reset инициализирует счётчик (на родителе или любом предке). counter-increment увеличивает его (обычно на нумеруемом элементе). counter(name) отображает текущее значение в content. Счётчик автоматически обрабатывает нумерацию по всему документу. Идеально для нумерации заголовков, элементов списка, рисунков или любого последовательного контента. Счётчики ограничены областью элемента, где они сброшены, и его потомками.
/* Initialize counter on parent */
body {
counter-reset: section;
}
/* Increment and display */
h2::before {
counter-increment: section;
content: "Section " counter(section) ": ";
color: #3498db;
font-weight: bold;
}
/* Result: Section 1: Introduction, Section 2: Methods... */Вложенные счётчики
Вложенные счётчики создают иерархичес кую нумерацию (1.1, 1.2, 2.1 и т. д.). Сбросьте субсчётчик на родительском заголовке (counter-reset: section на h2). Каждый h2 начинает новую главу и сбрасывает счётчик section. Каждый h3 увеличивает счётчик section в текущей главе. Свойство content объединяет несколько вызовов counter() с разделителями. Это отражает нумерацию книг и технических документов. Счётчики каскадируются естественно на основе структуры DOM и точек сброса.
body {
counter-reset: chapter;
}
h2 {
counter-reset: section;
counter-increment: chapter;
}
h2::before {
content: "Chapter " counter(chapter) ". ";
}
h3 {
counter-increment: section;
}
h3::before {
content: counter(chapter) "." counter(section) " ";
}
/* Result: Chapter 1. / 1.1 / 1.2 / Chapter 2. / 2.1 ... */Стили счётчиков
counter() принимает аргумент стиля: decimal (по умолчанию), decimal-leading-zero (01, 02), lower/upper-alpha (a/b или A/B), lower/upper-roman (i/ii или I/II). @counter-style определяет пользовательские стили сч ётчиков с циклическими символами, аддитивными системами или символической нотацией. system: cyclic повторяет символы; system: additive создаёт системы наподобие римских цифр. Пользовательские стили счётчиков мощны для интернационализированных списков, пользовательских маркеров или декоративной нумерации. Поддержка @counter-style браузерами хорошая в современных браузерах.
/* Different number styles */
ol {
counter-reset: item;
list-style: none;
}
li::before {
counter-increment: item;
/* decimal, decimal-leading-zero, lower-alpha,
upper-alpha, lower-roman, upper-roman */
content: counter(item, upper-roman) ". ";
}
/* Leading zeros: 01, 02, 03... */
li::before {
content: counter(item, decimal-leading-zero) ". ";
}
/* Custom @counter-style */
@counter-style thumbs {
system: cyclic;
symbols: "👍" "👎";
suffix: " ";
}
.thumbs-list { list-style-type: thumbs; }Функция counters() (вложенная)
Функция counters() (множественное число) возвращает полный путь счётчика как строку, с уровнями, разделёнными указанным разделителем. Это автоматически обрабатывает вложенные области счётчиков — каждый вложенный ol создаёт новую область. counters(nested, '.') производит 1, 1.1, 1.1.1 для глубоко вложенных списков. Это отличается от counter(), который показывает только текущий уровень. Используйте counters() для нумерации в стиле плана вложенных списков, оглавлений или иерархических меню. Строка-разделитель может быть любым символом (. , - > и т. д.).
/* counters() creates full path string */
ol {
counter-reset: nested;
list-style: none;
}
li::before {
counter-increment: nested;
/* counters() returns all levels separated by a string */
content: counters(nested, ".") " - ";
font-weight: bold;
}
/* For nested <ol> elements:
1 - Top level
1.1 - Nested
1.1.1 - Deeper
2 - Top level again
*/Счётчик для рисунков и таблиц
Счётчики идеально подходят для автоматической нумерации рисунков, таблиц и формул в документации. Сбрасывайте по главам (counter-reset на h2), чтобы нумерация начиналась заново каждой главой. Псевдоэлемент ::before на figcaption/caption добавляет номер в начало. Это обеспечивает последовательную автоматическую нумерацию, обновляющуюся при переупорядочивании контента. Для перекрёстных ссылок («см. Рисунок 3») CSS-счётчики напрямую не помогут — понадобится JS или HTML-якоря. Но для отображаемой нумерации это чистое, не требующее обслуживания решение.
body {
counter-reset: figure table;
}
figure figcaption::before {
counter-increment: figure;
content: "Figure " counter(figure) ": ";
font-weight: bold;
color: #2c3e50;
}
table caption::before {
counter-increment: table;
content: "Table " counter(table) ": ";
font-weight: bold;
color: #2c3e50;
}
/* Reset figure counter per chapter */
h2 {
counter-reset: figure;
}Стили печати
Основы @media print
@media print применяет стили только при печати. Скрывайте навигацию, рекламу, боковые панели и интерактивные элементы, не имеющие смысла на бумаге. Используйте !important для переопределения inline-стилей. print-color-adjust: exact заставляет браузеры печатать цвета фона и изображения (иначе они удаляются для экономии чернил). Используйте pt (пункты) для размеров шрифта при печати, а не px или rem. Установите width в 100% и уберите margins/padding для максимальной площади печати. Всегда тестируйте с предпросмотром печати (Ctrl+P).
@media print {
/* Hide non-essential elements */
nav, footer, .sidebar, .ads, .no-print {
display: none !important;
}
/* Ensure colors print */
* {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
/* Set base font for print */
body {
font-size: 12pt;
color: #000;
background: #fff;
}
/* Expand content to full width */
.content {
width: 100%;
margin: 0;
padding: 0;
}
}Разрывы страниц
Свойства разрыва страницы контролируют, где контент разделяется по страницам. break-before: page принудительно создаёт новую страницу перед элементом (используйте для глав). break-inside: avoid предотвращает разрыв таблиц, рисунков или блоков кода по страницам. break-after: avoid-page удерживает заголовок со следующим контентом. orphans и widows контролируют минимальное количество строк на границах страницы (orphans = внизу, widows = вверху). Свойства break-* — современный стандарт; page-break-* — устаревшие псевдонимы. Не все браузеры одинаково поддерживают все свойства — тестируйте тщательно.
@media print {
/* Force page break before */
h1, h2 {
page-break-before: always;
break-before: page; /* modern syntax */
}
/* Avoid breaking inside */
table, figure, pre, blockquote {
page-break-inside: avoid;
break-inside: avoid;
}
/* Keep with next (heading stays with content) */
h2, h3, h4 {
page-break-after: avoid;
break-after: avoid-page;
}
/* Avoid orphan/widow lines */
p {
orphans: 3; /* min lines at bottom of page */
widows: 3; /* min lines at top of page */
}
}Правило @page
@page настраивает саму печатную страницу: size (A4, letter, landscape или пользовательские размеры) и margins. Псевдоклассы :first, :left, :right стилизуют конкретные страницы — необходимы для книжной печати с разными полями для переплёта. Именованные страницы (@page cover) позволяют разным секциям иметь разные настройки; назначайте свойством page на элементе. Поля страницы создают область печати. Примечание: поддержка @page различается — Chrome хорошо поддерживает size и margins; у Firefox ограниченная поддержка. Для профессиональной печати рассмотрите специализированные инструменты вроде Prince XML.
/* Set page size and margins */
@page {
size: A4; /* or: letter, landscape, 8.5in 11in */
margin: 2cm;
}
/* First page different */
@page :first {
margin-top: 5cm; /* extra space for letterhead */
}
/* Left and right pages (for binding) */
@page :left { margin-left: 3cm; margin-right: 1.5cm; }
@page :right { margin-left: 1.5cm; margin-right: 3cm; }
/* Named pages for different sections */
@page cover {
margin: 0;
}
.cover-page {
page: cover;
}Ссылки, дружественные к печати
На бумаге ссылки бесполезны без их URL. Функция attr(href) в content отображает URL после текста каждой ссылки. Исключайте внутренние якорные ссылки (#) и ссылки javascript:. Аналогично abbr[title]::after показывает полное раскрытие аббревиатур. Это гарантирует, что печатные документы сохраняют информацию, которую гиперссылки предоставляют на экране. Используйте меньший, приглушённый шрифт для URL, чтобы отличить их от основного текста. Для длинных URL рассмотрите word-break: break-all для предотвращения переполнения.
@media print {
/* Show URL after links */
a[href]::after {
content: " (" attr(href) ")";
font-size: 0.9em;
color: #555;
}
/* Don't show URL for internal links */
a[href^="#"]::after,
a[href^="javascript:"]::after {
content: "";
}
/* Abbreviations: show full title */
abbr[title]::after {
content: " (" attr(title) ")";
}
}Колонтитулы печати
@page margin boxes (@top-center, @bottom-center и т. д.) помещают контент в поля страницы — идеально для номеров страниц, колонтитулов. counter(page) — текущая страница; counter(pages) — всего. Однако поддержка браузерами очень ограничена (в основном Prince XML и WeasyPrint; Chrome/Firefox не поддерживают margin boxes). Фолбэк использует элементы position: fixed, которые повторяются на каждой печатной странице в некоторых браузерах. Для надёжных колонтитулов печати во всех браузерах генерация PDF на стороне сервера (Puppeteer, wkhtmltopdf) часто надёжнее, чем CSS @page.
@page {
margin: 2cm 3cm;
/* Page counter in footer */
@bottom-center {
content: "Page " counter(page) " of " counter(pages);
font-size: 10pt;
color: #666;
}
/* Document title in header */
@top-center {
content: "My Document Title";
font-size: 10pt;
color: #666;
}
}
/* Note: @page margin boxes have limited browser
support. Chrome doesn't support them.
Use fixed-position elements as fallback: */
@media print {
.print-footer {
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
font-size: 10pt;
}
}Тёмный режим и цветовые схемы
prefers-color-scheme
prefers-color-scheme: dark обнаруживает предпочтение тёмного режима ОС/браузера пользователя. Определите переменные темы в :root для светлого режима, затем переопределите их в media query для тёмного. Использование CSS-переменных означает, что меняются только значения переменных — все компоненты обновляются автоматически. Это стандартный подход для тёмного режима. Media query также поддерживает 'light' и 'no-preference'. Тестируйте, переключая тёмный режим ОС или используя вкладку Rendering в Chrome DevTools (Emulate CSS prefers-color-scheme).
/* Light mode (default) */
:root {
--bg: #ffffff;
--text: #1a1a1a;
--surface: #f5f5f5;
--border: #e0e0e0;
--accent: #3498db;
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #e0e0e0;
--surface: #2a2a2a;
--border: #404040;
--accent: #5dade2;
}
}
body {
background: var(--bg);
color: var(--text);
}Ручное переключение темы
Для ручного переключения темы используйте атрибут data-theme на <html>, переопределяющий системное предпочтение. Сохраняйте выбор пользователя в localStorage для сохранения. При загрузке страницы проверяйте localStorage перед рендерингом, чтобы избежать мерцания неправильной темы (FOUC). color-scheme: light dark сообщает браузеру рендерить нативные UI-элементы (полосы прокрутки, элементы форм) в соответствующей схеме. Для лучшего UX по умолчанию используйте системное предпочтение, когда нет сохранённого выбора: проверяйте matchMedia('(prefers-color-scheme: dark)') как фолбэк.
/* Default to system preference, allow override */
:root {
color-scheme: light dark;
}
/* Manual dark mode via data attribute */
[data-theme="dark"] {
--bg: #1a1a1a;
--text: #e0e0e0;
}
[data-theme="light"] {
--bg: #ffffff;
--text: #1a1a1a;
}
/* Toggle button */
<button onclick="toggleTheme()">Toggle Theme</button>
<script>
function toggleTheme() {
const current = document.documentElement.dataset.theme;
const next = current === "dark" ? "light" : "dark";
document.documentElement.dataset.theme = next;
localStorage.setItem("theme", next);
}
// Load saved preference
const saved = localStorage.getItem("theme");
if (saved) document.documentElement.dataset.theme = saved;
</script>Свойство color-scheme
Свойство color-scheme сообщает браузеру, какие цветовые схемы поддерживает страница, влияя на нативные UI-элементы: полосы прокрутки, элементы форм (inputs, buttons, dropdowns), цвет фона/холста по умолчанию и цвет ::placeholder. Без него элементы форм могут отображаться светлыми даже в тёмном режиме (резкое несоответствие). Установите color-scheme: light dark на :root, чтобы нативные элементы адаптировались автоматически. Это однострочное исправление, значительно улучшающее отделку тёмного режима. Оно о тделено от темизации CSS-переменных — влияет только на нативный рендеринг браузера.
:root {
/* Tell browser this page supports both schemes */
color-scheme: light dark;
}
/* Force light only */
.light-only {
color-scheme: light;
}
/* Force dark only */
.dark-only {
color-scheme: dark;
}
/* This affects native UI: scrollbars, form controls,
default colors (canvas, text), and form elements */
input, textarea, select {
/* Will use dark form controls in dark mode */
color-scheme: dark;
}Изображения и медиа в тёмном режиме
Изображения требуют особой обработки в тёмном режиме. Диаграммы с белым фоном можно инвертировать filter: invert(1) hue-rotate(180deg) — hue-rotate предотвращает искажение цветов. Фотографии людей никогда не следует инвертировать. Слегка уменьшите яркость для меньшего напряжения глаз. Для логотипов и иконок используйте <picture> с источниками media-query для тёмных оптимизированных версий. SVG с currentColor адаптируются автоматически. Для фоно вых изображений предоставляйте тёмные альтернативы через media queries. Всегда тестируйте изображения в обоих режимах на читаемость.
@media (prefers-color-scheme: dark) {
/* Invert images that are essentially white-background */
img[src*="logo"],
img[src*="diagram"] {
filter: invert(1) hue-rotate(180deg);
}
/* Reduce brightness of photos */
img.photo {
filter: brightness(0.85);
}
/* Don't invert photos with people */
img[src*="photo"]:not(.invertable) {
filter: none;
}
}
/* Use dark-mode-friendly SVG */
<picture>
<source srcset="logo-dark.svg"
media="(prefers-color-scheme: dark)">
<img src="logo-light.svg" alt="Logo">
</picture>Плавный переход темы
Добавление цветовых переходов ко всем элементам создаёт плавную анимацию переключения темы. Однако это может вызвать мерцание анимированных цветов при начальной загрузке страницы. Решение: добавьте класс .no-transition на <html> во время переключений темы и при начальной загрузке, затем удалите через кадр. Всегда уважайте prefers-reduced-motion, чтобы отключить переходы для пользователей, запросивших уменьшенное движение. Будьте избирательны — переход всех свойств может влиять на производительность. Переходите только свойства, связанные с цветом (background-color, color, border-color, box-shadow).
/* Transition colors when theme changes */
* {
transition: background-color 0.3s ease,
color 0.3s ease,
border-color 0.3s ease;
}
/* But not during initial load (prevent flash) */
.no-transition * {
transition: none !important;
}
<script>
// Disable transitions during theme switch
function toggleTheme() {
document.documentElement.classList.add("no-transition");
document.documentElement.dataset.theme = newTheme;
// Re-enable after switch
requestAnimationFrame(() => {
document.documentElement.classList.remove("no-transition");
});
}
</script>
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
* { transition: none !important; }
}Углублённое изучение функций CSS
clamp(), min(), max()
clamp(min, preferred, max) — священный Грааль жидкой типографики и интервалов — масштабируется с предпочтительным значением, но никогда не выходит за границы min/max. min() выбирает меньшее значение (отлично для адаптивных max-width). max() выбирает большее (отлично для минимальных размеров). Они устраняют многие media queries, обеспечивая непрерывное жидкое масштабирование. Предпочтительное значение в clamp обычно использует viewport-единицы (vw, vh) или единицы контейнера (cqw). Это современный подход к адаптивному дизайну без брейкпоинтов.
/* clamp(min, preferred, max) */
h1 {
font-size: clamp(1.5rem, 5vw, 3rem);
/* Never smaller than 1.5rem, never larger than 3rem,
preferred is 5vw */
}
/* min(): use the smaller value */
.container {
width: min(100% - 2rem, 1200px);
/* Full width minus padding, but max 1200px */
}
/* max(): use the larger value */
.hero {
font-size: max(2rem, 4vw);
/* At least 2rem, grows with viewport */
}
/* Combining for responsive padding */
.card {
padding: clamp(1rem, 3vw, 2rem);
}Продвинутый calc()
calc() выполняет вычисления со смешанными единицами (px, %, em, vw и т. д.). Сложение и вычитание требуют пробелов вокруг оператора (calc(100% - 20px), а не calc(100%-20px)). Умножение и деление работают только с числами, не длинами (calc(10px * 2) допустимо; calc(10px * 10px) — нет). calc() можно вкладыв ать, но скобки также работают. Сочетание с CSS-переменными позволяет динамические вычисляемые дизайн-системы. Необходимо для адаптивных макетов, сочетающих фиксированные и жидкие размерности.
/* Mixed units */
.sidebar {
width: calc(100% - 250px); /* full width minus fixed sidebar */
height: calc(100vh - 60px); /* viewport minus header */
}
/* Nested calc */
.element {
margin: calc(calc(100% - 50px) / 2);
/* Simplified: */
margin: calc((100% - 50px) / 2);
}
/* With CSS variables */
:root { --gap: 20px; }
.grid {
gap: calc(var(--gap) * 2);
}
/* Math operations: + - * / */
.padding {
padding: calc(1rem + 2px);
width: calc(50% - 10px);
}Функция attr()
attr() извлекает значения атрибутов HTML, чаще всего используется в content для псевдоэлементов. Это основа CSS-only тултипов (атрибут data-tooltip → content ::after). attr() надёжно работает только в свойстве content. Использование attr() для других свойств (width, color и т. д.) с приведением типов (attr(data-size px)) — часть CSS Values Level 5, но с минимальной п оддержкой браузерами. Пока используйте CSS-переменные для динамических значений из HTML: установите style='--size: 100px' и используйте var(--size).
/* Display attribute values */
a[href]::after {
content: attr(href);
}
/* data attributes for tooltips */
.tooltip::after {
content: attr(data-tooltip);
display: none;
position: absolute;
}
.tooltip:hover::after {
display: block;
}
/* HTML: <div class="tooltip" data-tooltip="Help text">?</div> */
/* Note: attr() for non-content properties is experimental */
/* This doesn't work widely yet: */
/* .box { width: attr(data-width px); } */var() с фолбэками
var() принимает фолбэк как второй аргумент, используемый, когда переменная не определена. Фолбэки можно объединять в цепочку: var(--a, var(--b, var(--c, default))). Это позволяет прогрессивное улучшение и многоуровневую темизацию. Пустое значение переменной (--defined: ;) всё ещё считается «установленным», поэтому фолбэк не сработает — это позволяет условные CSS-паттерны. Фолбэки могут включать любое допустимое CSS-значение, включая calc() и другие функции. Используйте фолбэки для опциональных тематических переменных, которые могут быть не определены во всех контекстах.
/* Single fallback */
color: var(--text-color, #333);
/* Chained fallbacks */
color: var(--text, var(--default-text, black));
/* Fallback in shorthand */
background: var(--bg, white) url(image.png);
/* Fallback with calc */
width: calc(100% - var(--sidebar, 250px));
/* Check if variable is set */
:root {
--defined: ; /* empty space = valid value */
}
.conditional {
--use-default: var(--defined, initial);
/* If --defined is empty, --use-default = initial */
}Функции градиентов
CSS-градиенты создают плавные переходы между цветами без изображений. linear-gradient (направление + цвета), radial-gradient (от центра наружу) и conic-gradient (вращение вокруг точки) — три типа. Жёсткие стопы (одинаковый процент для двух цветов) создают резкие полосы. repeating-linear-gradient создаёт паттерны вроде полос. Градиенты можно наслаивать с несколькими фонами (через запятую, первый = верхний слой). Conic-градиенты позволяют круговые диаграммы и цветовые круги на чистом CSS. Градиенты независимы от разрешения и работают лучше, чем файлы изображений.
/* Linear gradient */
.bg { background: linear-gradient(45deg, #3498db, #e74c3c); }
.bg2 { background: linear-gradient(to right, red, orange, yellow); }
/* Radial gradient */
.radial { background: radial-gradient(circle, white, blue); }
.radial2 { background: radial-gradient(circle at top left, #fff, #000); }
/* Conic gradient */
.pie { background: conic-gradient(red 0% 30%, blue 30% 70%, green 70% 100%); }
/* Repeating gradients */
.stripes { background: repeating-linear-gradient(45deg, #ccc 0 10px, #fff 10px 20px); }
/* Hard color stops (sharp edges) */
.hard { background: linear-gradient(to right, #000 50%, #fff 50%); }
/* Multiple backgrounds */
.multi {
background:
linear-gradient(rgba(0,0,0,0.5), transparent),
url(photo.jpg);
}CSS-переменные
Определение и использование переменных
Пользовательские свойства CSS (переменные) определяются с --name и используются с var(). Объявляйте их на :root для глобального доступа. В отличие от переменных Sass, они живые — изменение переменной через JavaScript мгновенно обновляет каждый элемент, использующий её.
:root {
--primary: #3498db;
--spacing: 16px;
--radius: 8px;
}
.button {
background: var(--primary);
padding: var(--spacing);
border-radius: var(--radius);
}Значения фолбэка
var() принимает второй аргумент как фолбэк, когда переменная не определена. Фолбэки можно вкладывать, позволяя объединять значения по умолчанию в цепочку. Для браузеров, не поддерживающих пользовательские свойства, объявите обычное значение сначала, затем переопределите с var().
.box {
color: var(--brand, #333);
background: var(--bg, var(--default-bg, white));
}
/* Provide fallback for older browsers */
.element {
color: #333;
color: var(--text-color, #333);
}Взаимодействие с JavaScript
JavaScript читает переменные через getComputedStyle().getPropertyValue() и устанавливает их через setProperty(). Это делает темизацию во время выполнения тривиальной — переключите несколько переменных для рестайлинга всего приложения.
// Read a variable
const color = getComputedStyle(document.documentElement)
.getPropertyValue('--primary');
// Set a variable on an element
document.documentElement.style.setProperty('--primary', '#e74c3c');
// Toggle a theme
document.documentElement.setAttribute('data-theme', 'dark');Область видимости и наследование
Пользовательские свойства наследуются, поэтому переменная, установленная на :root, каскадируется на каждый элемент. Переопределение на .card переопределяет только для этого поддерева. Это ограничение области видимости позволяет темизацию на уровне компонента без войн специфичности.
:root { --size: 16px; }
.card {
--size: 20px; /* Overrides only within .card */
font-size: var(--size);
}
.card .text { font-size: var(--size); } /* 20px */
.other { font-size: var(--size); } /* 16px */Адаптивные переменные
Переопределяйте переменные внутри media queries, чтобы весь макет реагировал на брейкпоинты без переписывания правил. Измените одну переменную, и каждый использующий её элемент адаптируется — намного чище, чем переопределять каждое свойство отдельно.
:root {
--container-width: 1200px;
--font-size: 18px;
}
@media (max-width: 768px) {
:root {
--container-width: 100%;
--font-size: 16px;
}
}
.container { max-width: var(--container-width); }
body { font-size: var(--font-size); }Анимации и ключевые кадры
@keyframes и сокращение animation
@keyframes определяет начальное (from/0%) и конечное (to/100%) состояния анимации. Сокращение animation объединяет name, duration, timing-function, delay, iteration-count, direction, fill-mode и play-state. forwards сохраняет конечное состояние.
@keyframes slide-in {
from { transform: translateX(-100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
.element {
animation: slide-in 0.5s ease-out forwards;
}
/* Shorthand: name duration timing delay count direction fill-mode */
.bounce {
animation: bounce 1s cubic-bezier(0.68, -0.55, 0.27, 1.55) 0s infinite alternate;
}Переходы
Переходы плавно интерполируют между значениями свойств при их изменении. Укажите, какие свойства переходят, и их длительность/тайминг. transition: all удобно, но может повредить производительности — предпочитайте перечисление конкретных свойств.
.button {
background: blue;
transition: background 0.3s ease, transform 0.2s ease-out;
}
.button:hover {
background: darkblue;
transform: scale(1.05);
}
/* Transition all changed properties */
.card { transition: all 0.3s ease; }Функции тайминга
Функции тайминга управляют кривой ускорения. ease-out начинается быстро и замедляется (хорошо для появлений); ease-in начинается медленно и ускоряется (хорошо для исчезновений). cubic-bezier позволяет создавать пользовательские кривые. steps() создаёт дискретный эффект.
/* Built-in */
.ease-linear { transition: all 1s linear; }
.ease-out { transition: all 1s ease-out; }
/* Custom cubic-bezier(x1, y1, x2, y2) */
.custom { transition: all 1s cubic-bezier(0.68, -0.55, 0.27, 1.55); }
/* Steps */
.steps { transition: all 1s steps(5, end); }События анимации
JavaScript может слушать animationstart, animationiteration (срабатывает на каждом цикле) и animationend. Управляйте воспроизведением через свойство animation-play-state (paused/running). Комбинируйте с animationend для цепочки анимаций.
const el = document.querySelector('.animate');
el.addEventListener('animationstart', () => console.log('Started'));
el.addEventListener('animationiteration', () => console.log('Looped'));
el.addEventListener('animationend', () => console.log('Finished'));
// Pause and resume
el.style.animationPlayState = 'paused';
el.style.animationPlayState = 'running';Производительность: transform и opacity
Анимируйте только transform и opacity для производительности 60fps — они работают на GPU без запуска перерасчёта макета. Анимация width, height, top или margin заставляет пересчитывать макет каждый кадр. Используйте will-change умеренно для подсказки браузеру.
/* GOOD: GPU-accelerated, cheap */
.fade { transition: opacity 0.3s; }
.move { transition: transform 0.3s; }
/* BAD: triggers layout/paint, expensive */
.resize { transition: width 0.3s, height 0.3s; }
/* Hint the browser to optimize */
.card { will-change: transform; }3D-трансформации
2D-трансформации
2D-трансформации перемещают, вращают, масштабируют и искажают элементы, не затрагивая окружающий макет. Трансформации композируются на GPU, что делает их прои зводительными для анимации. Порядок важен: rotate затем translate двигает вдоль повёрнутой оси.
.box { transform: translate(50px, 20px); }
.box { transform: rotate(45deg); }
.box { transform: scale(1.5); }
.box { transform: skew(20deg, 10deg); }
/* Combined — order matters */
.combo { transform: translate(100px, 0) rotate(90deg) scale(1.2); }3D-трансформации и перспектива
3D-трансформации добавляют rotateX, rotateY, rotateZ, translateZ и scaleZ. perspective на родителе даёт всем дочерним элементам общую точку схода — меньшие значения усиливают 3D-эффект. transform-style: preserve-3d сохраняет вложенные элементы в 3D.
.scene { perspective: 1000px; }
.card {
transform: rotateY(45deg);
transform-style: preserve-3d;
}
/* Perspective on the element itself */
.self { transform: perspective(800px) rotateX(30deg); }
/* Transform origin */
.pivot { transform-origin: top left; transform: rotate(45deg); }Эффект flip-карточки
Классическая flip-карточка использует preserve-3d на контейнере, с передним и задним лицами, абсолютно позиционированными. backface-visibility: hidden скрывает заднюю сторону каждого лица. Заднее лицо предварительно повёрнуто на 180deg, поэтому отображается при перевороте контейнера.
.card { perspective: 1000px; }
.inner {
position: relative;
transform-style: preserve-3d;
transition: transform 0.6s;
}
.card:hover .inner { transform: rotateY(180deg); }
.front, .back {
position: absolute;
backface-visibility: hidden;
}
.back { transform: rotateY(180deg); }Куб с 6 гранями
3D-куб строится из шести граней, каждая сдвинута на 50px (половина ширины куба) наружу вдоль своей оси. preserve-3d сохраняет грани в 3D-пространстве. Анимируйте transform куба для вращения.
.cube {
position: relative;
width: 100px; height: 100px;
transform-style: preserve-3d;
transform: rotateX(-20deg) rotateY(30deg);
}
.face { position: absolute; width: 100px; height: 100px; }
.front { transform: translateZ(50px); }
.back { transform: rotateY(180deg) translateZ(50px); }
.right { transform: rotateY(90deg) translateZ(50px); }
.left { transform: rotateY(-90deg) translateZ(50px); }
.top { transform: rotateX(90deg) translateZ(50px); }
.bottom { transform: rotateX(-90deg) translateZ(50px); }Matrix и производительность
matrix() и matrix3d() выражают любую трансформацию как единую матрицу — полезно при вычислении трансформаций в JavaScript. translateZ(0) или will-change: transform принудительно включают GPU-ускорение, улучшая плавность анимации. Избегайте чрезмерного использования слоёв.
/* matrix(a, b, c, d, e, f) encodes translate, rotate, scale, skew */
.transformed { transform: matrix(1, 0, 0, 1, 100, 50); }
/* 3D matrix */
.cube { transform: matrix3d(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1); }
/* Hardware acceleration hint */
.gpu { transform: translateZ(0); }Продвинутый CSS Grid
Области шаблона Grid
grid-template-areas именует ячейки сетки визуально, делая намерение макета очевидным. Каждая строка в кавычках представляет ряд сетки; одинаковые имена охватывают ячейки. Пустые ячейки используют точку (.). Области должны образовывать прямоугольники — L-формы недопустимы.
.layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
gap: 16px;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }minmax и auto-fit
repeat(auto-fit, minmax(200px, 1fr)) создаёт адаптивную сетку без media queries: колонки минимум 200px и растягиваются для заполнения ряда. auto-fit сворачивает пустые треки; auto-fill оставляет их зарезервированными.
.grid {
display: grid;
/* Columns auto-fit, each at least 200px, sharing leftover space */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
/* Fixed number that wraps */
.wrap {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}Выравнивание Grid
Выравнивание Grid работает на двух уровнях: контейнер (justify/align-items и justify/align-content) и элемент (justify/align-self). justify-* контролирует inline-ось, align-* — block-ось. place-items: center — сокращение для обоих.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
justify-items: center;
align-items: center;
justify-content: space-between;
align-content: center;
}
.item {
justify-self: end;
align-self: start;
}Охват и размещение по линиям
Размещайте элементы по номеру линии grid (1 / 3 означает от линии 1 до линии 3) или по охвату (span 2). Отрицательные линии считаются с конца (-1 — последняя линия). grid-column: 1 / -1 заставляет элемент охватывать всю ширину.
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
.header { grid-column: 1 / -1; } /* full width */
.sidebar { grid-column: 1 / 3; } /* columns 1-2 */
.main { grid-column: span 2; } /* span 2 columns */
.tall { grid-row: 1 / span 2; } /* span 2 rows */Subgrid
subgrid позволяет дочерней сетке наследовать определения треков родителя, поэтому колонки или ряды идеально выравниваются между вложенными сетками. Без subgrid вложенные сетки определяют свои треки и могут рассогласоваться. Поддерживается во всех современных браузерах с 2023 года.
.card {
display: grid;
grid-template-rows: auto 1fr auto;
/* Inherit column tracks from parent */
grid-template-columns: subgrid;
grid-column: span 3;
}
.parent {
display: grid;
grid-template-columns: repeat(6, 1fr);
}Функции CSS
calc()
calc() выполняет математические операции со смешанными единицами (px, %, vw, em, rem). Всегда окружайте + и - пробелами (calc(100%-20px) не работает; calc(100% - 20px) работает). Умножение и деление требуют, чтобы одна сторона была числом без единиц.
.sidebar {
width: calc(100% - 250px);
padding: calc(16px + 2vw);
font-size: calc(14px + 0.5vw);
}
/* Nested calc */
.full { width: calc(calc(100% - 20px) / 2); }
/* With variables */
.box { margin: calc(var(--gap) * 2); }clamp()
clamp() ограничивает значение между минимумом и максимумом с предпочтительным значением посередине. Это самый чистый способ построить жидкую типографику и интервалы — значение масштабируется, но никогда не выходит ниже или выше границ.
/* clamp(MIN, PREFERRED, MAX) */
h1 {
font-size: clamp(1.5rem, 4vw, 3rem);
}
.sidebar {
width: clamp(200px, 25vw, 400px);
}
.padding {
padding: clamp(1rem, 2vw + 1rem, 2rem);
}min() и max()
min() возвращает наименьший аргумент; max() возвращает наибольший. В отличие от clamp (который принимает ровно три значения), min/max принимают любое количество аргументов. Используйте min() для ограничения значения сверху, max() — снизу.
/* Picks the smallest/largest of comma-separated values */
.box {
width: min(100% - 32px, 800px);
font-size: max(16px, 2vw);
}
/* With multiple units */
.responsive {
padding: min(5vw, 2rem);
margin: max(1rem, 3vw);
}var() с фолбэками
Второй аргумент var() используется только тогда, когда пользовательское свойство не определено. Для недопустимых значений свойство откатывается к унаследованному или начальному значению. Вкладывайте вызовы var() для цепочки фолбэков.
.button {
background: var(--btn-bg, #007bff);
color: var(--text, var(--default-text, black));
width: var(--width, 100%);
}color-mix() и цветовые функции
color-mix() смешивает два цвета в указанном цветовом пространстве (srgb, oklch, lab). Это современный способ получить оттенки и тона из фирменного цвета. Синтаксис относительных цветов (ключевое слово from) позволяет регулировать отдельные каналы.
/* Mix two colors */
.muted { color: color-mix(in srgb, red 30%, blue); }
/* Adjust relative lightness */
.lighter { background: color-mix(in oklch, var(--brand) 70%, white); }
.darker { background: color-mix(in oklch, var(--brand) 70%, black); }
/* Relative color syntax (newest) */
.tint { background: oklch(from var(--brand) calc(l + 0.1) c h); }Scroll-driven анимации
Основы animation-timeline
animation-timeline: scroll(root) привязывает анимацию к позиции прокрутки страницы — по мере прокрутки пользователем анимация прогрессирует. JavaScript не требуется. Это намного плавнее, чем слушатели событий прокрутки.
@keyframes grow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.progress-bar {
animation: grow linear;
animation-timeline: scroll(root);
transform-origin: left;
}Таймлайн view()
view() создаёт таймлайн, привязанный к появлению и исчезновению элемента из viewport. animation-range контролирует, какая часть входа запускае т анимацию. Идеально для эффектов появления при прокрутке без IntersectionObserver.
@keyframes reveal {
from { opacity: 0; transform: translateY(40px); }
to { opacity: 1; transform: translateY(0); }
}
.card {
animation: reveal linear;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}Полоса прогресса, связанная с прокруткой
Фиксированная полоса прогресса, заполняющаяся при прокрутке страницы. animation-timeline: scroll(root) отображает прокрутку страницы на диапазон анимации 0-100%. transform-origin: 0 50% заставляет полосу расти слева.
body { min-height: 200vh; }
.progress {
position: fixed;
top: 0; left: 0;
height: 4px;
width: 100%;
background: linear-gradient(to right, #6366f1, #ec4899);
transform-origin: 0 50%;
animation: scale-x linear;
animation-timeline: scroll(root);
}
@keyframes scale-x {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}Именованные таймлайны прокрутки
Когда нужно привязать анимацию к конкретному прокручиваемому элементу (не корневому), определите scroll-timeline-name на контейнере и ссылайтесь на него в animation-timeline. scroll-timeline-axis выбирает, какую ось отслеживать (block/inline/x/y).
.scroll-container {
scroll-timeline-name: --cards;
scroll-timeline-axis: block;
overflow-y: scroll;
height: 400px;
}
.card {
animation: fade-in linear;
animation-timeline: --cards;
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}Регулировки диапазона view
animation-range точно настраивает, когда анимация выполняется относительно видимости элемента. cover 0% до cover 100% охватывает от входа элемента до полного исч езновения. contain 0% до 100% охватывает только когда элемент полностью внутри.
.image {
animation: parallax linear;
animation-timeline: view();
animation-range: cover 0% cover 100%;
}
@keyframes parallax {
from { transform: translateY(-20%); }
to { transform: translateY(20%); }
}
/* Only animate while in view */
.pulse {
animation: pulse 1s infinite;
animation-timeline: view();
animation-range: contain 0% contain 100%;
}CSS-слои
Определение @layer
@layer группирует стили в именованные слои каскада. Порядок в строке объявления задаёт приоритет: более поздние слои побеждают более ранние, независимо от специфичности селектора. Это приносит предсказуемое упорядочивание в большие таблицы стилей.
@layer reset, base, components, utilities;
@layer reset {
* { margin: 0; padding: 0; box-sizing: border-box; }
}
@layer base {
body { font-family: sans-serif; line-height: 1.6; }
}
@layer utilities {
.hidden { display: none !important; }
}Приоритет слоёв
Внутри слоёв применяются обычные правила специфичности. Но между слоями более поздние всегда побеждают — единственный класс в components побеждает ID в base. Неслоевые стили имеют приоритет над всеми слоевыми стилями.
@layer base, components;
@layer base {
/* Even though this is more specific, base loses */
div.button { color: black; }
}
@layer components {
.button { color: blue; } /* This wins */
}
/* Unlayered styles always beat layered styles */
.button { color: red; }Импорт в слои
@import ... layer(name) помещает всю таблицу стилей в именованный слой. Это необходимо для укрощения стороннего CSS: поместите Bootstrap в слой vendor, и ваши пользовательские стили (в более позднем слое или неслойные) всегда победят.
@import url("reset.css") layer(reset);
@import url("bootstrap.css") layer(vendor);
@layer reset, vendor, custom;
@layer custom {
.btn { background: hotpink; }
}Вложенные слои
Слои могут вкладываться, создавая иерархию. framework.theme ссылается на подслой theme внутри framework. Вложенные слои следуют тем же правилам приоритета: внутри framework theme побеждает base.
@layer framework {
@layer base, theme;
@layer base {
.btn { padding: 8px; }
}
@layer theme {
.btn { color: navy; }
}
}
/* Reference nested layer */
@layer framework.theme {
.btn { color: hotpink; }
}Условная логика слоёв
Блоки @layer можно вкладывать внутрь @media и @supports, поэтому принадлежность к слою адаптируется к условиям. Это удерживает специфичные для функций стили организованными внутри их слоя, а не утекает в неслойное пространство.
@layer base, components;
@media (max-width: 600px) {
@layer components {
.nav { flex-direction: column; }
}
}
@supports (display: grid) {
@layer components {
.layout { display: grid; }
}
}Backdrop-фильтры
Основы backdrop-filter
backdrop-filter применяет фильтр к контенту позади элемента, создавая эффекты матового стекла. Всегда включайте префикс -webkit- для Safari. Комбинируйте с полупрозрачным фоном, чтобы размытие было видно.
.glass {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 12px;
}Несколько фильтров
Объединяйте несколько фильтров через пробел: blur, brightness, contrast, grayscale, hue-rotate, invert, opacity, saturate, sepia. Каждый применяется к фону. Сочетайте с тинтным фоном для управления настроением.
.vibrant {
backdrop-filter: blur(8px) saturate(180%) brightness(1.1);
}
.dark-glass {
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(12px) grayscale(50%);
}Режимы смешивания
mix-blend-mode управляет, как элемент смешивается с контентом под ним (отдельно от backdrop-filter). multiply затемняет, screen осветляет, difference инвертирует. Будьте осторожны с читаемостью текста.
.overlay {
background: rgba(255, 0, 100, 0.3);
backdrop-filter: blur(4px);
mix-blend-mode: multiply;
}
.difference {
background: white;
mix-blend-mode: difference;
backdrop-filter: invert(1);
}Производительность и фолбэки
backdrop-filter требователен к GPU, особенно при больших радиусах размытия. Предоставьте сплошной фоновый фолбэк для браузеров без поддержки, затем улучшайте с @supports. Избегайте анимации backdrop-filter — может вызывать серьёзные рывки.
/* Fallback for unsupported browsers */
.glass {
background: rgba(255, 255, 255, 0.9);
}
/* Progressive enhancement */
@supports (backdrop-filter: blur(10px)) {
.glass {
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(10px);
}
}Карточка в стиле стеклообразности
Классическая карточка стеклообразности сочетает полупрозрачный фон, размытие фона, лёгкую светлую границу (имитирующую отражение краёв) и мягкую тень. Фильтр saturate усиливает цвета, видимые сквозь стекло.
.card {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(16px) saturate(180%);
-webkit-backdrop-filter: blur(16px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 16px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}Связанные сниппеты CSS
Copy-paste ready code for common tasks.
Центрирование div с Flexbox и Grid
Центрирование элементов по горизонтали и вертикали с помощью Flexbox, Grid и абсолютного позиционирования.
CSS-селекторы и псевдоклассы
Выбор элементов с помощью псевдоклассов, псевдоэлементов и селекторов атрибутов.
CSS Grid
Сеточная вёрстка.
Flexbox
Вёрстка гибкими блоками.
Анимация
CSS-анимации keyframe.
Переходы
CSS-эффекты перехода.
CSS-переменные
Пользовательские свойства.
Медиа-запросы
Адаптивные точки останова.
Псевдоклассы и псевдоэлементы
Псевдоклассы и псевдоэлементы.
Адаптивный дизайн
Техники адаптивной вёрстки.
Тёмный режим
Переключение тёмной темы.
Пользовательский скроллбар
Стилизация скроллбаров.
Градиенты
Линейные и радиальные градиенты.
Эффекты теней
box-shadow и text-shadow.
Фильтры
CSS-эффекты фильтров.
Трансформации
2D/3D-трансформации.
Was this helpful?