Skip to content

CSS Шпаргалка

Язык таблиц стилей для описания представления веб-страниц.

01

Селекторы

Базовые селекторы

CSS-селекторы нацеливают HTML-элементы для стилизации. Type-селекторы сопоставляют имена элементов (p, div). Class-селекторы (.) сопоставляют атрибуты class и могут переиспользоваться. ID-селекторы (#) сопоставляют один элемент и должны быть уникальными. Универсальный селектор (*) сопоставляет всё. Группируйте селекторы запятыми для совместного использования стилей.

css
/* 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 после. Понимание комбинаторов необходимо для точной стилизации без лишних классов.

css
/* 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-атрибутами без добавления лишних классов.

css
/* 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-й элемент. Снижают потребность в лишних классах.

css
/* 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 для блочного поведения.

css
/* 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) — хак — избегайте.

css
/* 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; }
02

Блочная модель

Margin и Padding

Каждый элемент — коробка с контентом, padding, border и margin. Padding внутри border (влияет на фон), margin снаружи (прозрачный). Shorthand: 1 значение = все стороны, 2 = верх/низ лево/право, 3 = верх лево/право низ, 4 = верх право низ лево (по часовой). margin: 0 auto центрирует блочные элементы с определённой шириной.

css
.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. Всегда предоставляйте видимые стили фокуса для доступности.

css
.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'ов.

css
/* 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 для полупрозрачных теней, которые смешиваются естественно. Тени хороши для глубины, но избыток вредит производительности — предпочитайте тонкие тени.

css
/* 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 для переключения контента.

css
/* 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; }
03

Цвета и единицы

Форматы цвета

CSS поддерживает несколько форматов цвета. Hex (#rrggbb) наиболее распространён. RGB/RGBA добавляет alpha-прозрачность. HSL (Hue 0-360, Saturation%, Lightness%) интуитивен — меняйте hue для сдвига цвета, lightness для оттенков. currentColor наследует свойство color элемента. Современный CSS также поддерживает oklch() и color() для более широких gamut'ов. Используйте rgba/hsla для прозрачности.

css
/* 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 для границ/тонких деталей.

css
/* 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.

css
/* 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.

css
/* 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). Фильтры могут влиять на производительность — используйте умеренно.

css
/* 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;
}
04

Типографика

Свойства шрифта

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.

css
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).

css
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 для работы обрезки.

css
/* 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>. Предзагружайте критические шрифты для производительности.

css
@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. Это обеспечивает правильный каскад состояний ссылок.

css
/* 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; }
05

Макет Flexbox

Flex-контейнер

display: flex создаёт flex-контейнер. justify-content выравнивает элементы по главной оси (горизонтально в row). align-items выравнивает по поперечной оси (вертикально). flex-direction меняет главную ось. flex-wrap позволяет элементам переноситься на новые строки. gap устанавливает интервал между элементами (заменяет margin-хаки). Flexbox идеален для 1D-макетов (строки ИЛИ столбцы).

css
.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.

css
.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.

css
/* 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). Этот шаблон создаёт адаптивные сетки карточек.

css
/* 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.

css
.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 */
}
06

Макет CSS Grid

Контейнер Grid

display: grid создаёт двумерный макет. grid-template-columns определяет размеры колонок: единицы fr распределяют свободное пространство пропорционально. repeat(3, 1fr) создаёт 3 равные колонки. repeat(auto-fit, minmax(250px, 1fr)) создаёт адаптивную сетку, которая автоматически регулирует количество колонок — это «священный Грааль» адаптивных сеток. gap заменяет margins для интервалов.

css
.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 позволяет точное двумерное размещение — элементы могут охватывать одновременно и строки, и колонки.

css
.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 для адаптивных макетов.

css
.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 объединяет оба выравнивания контента.

css
.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 контейнера для оптимальных результатов.

css
/* 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 */
07

Позиционирование

Типы позиционирования

static — значение по умолчанию (нормальный поток документа). relative смещает относительно нормальной позиции, не затрагивая другие элементы (создаёт контекст позиционирования). absolute удаляет из потока и позиционирует относительно ближайшего позиционированного предка. fixed позиционирует относительно viewport (остаётся при прокрутке). sticky переключается между relative и fixed — прокручивается нормально, затем «прилипает» на пороге.

css
/* 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%). Абсолютное позиционирование удаляет элементы из нормального потока.

css
/* 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 для предотвращения проблем с наложением.

css
/* 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. Это частый источник путаницы.

css
/* 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.

css
/* 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 */
08

Адаптивный дизайн

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 на мобильных.

css
/* 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 для читаемости.

css
/* 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 — компоненты адаптируются к своему контейнеру, а не к экрану. Отлично для переиспользуемых компонентов в разных контекстах вёрстки.

css
/* 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 для обрезки изображений в фиксированных размерах.

css
/* 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 браузера.

css
/* 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 */
09

Переходы и анимации

Переходы

transition создаёт плавные изменения между значениями свойств. Синтаксис: transition: property duration timing-function delay. Функции тайминга: ease (по умолчанию), linear, ease-in, ease-out, ease-in-out, cubic-bezier(). Переходят только анимируемые свойства. Избегайте transition: all (производительность). Переходы срабатывают при изменениях псевдоклассов (:hover, :focus) или классов через JavaScript.

css
.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 меняет точку вращения.

css
.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 сохраняет конечное состояние. Анимации запускаются автоматически, в отличие от переходов.

css
@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;.

css
.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 — некоторые пользователи испытывают укачивание или имеют вестибулярные расстройства. Предоставляйте мгновенные переходы или отключайте анимации для этих пользователей.

css
/* 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;
  }
}
10

Переменные и продвинутые функции

Пользовательские свойства (переменные)

Пользовательские свойства (CSS-переменные) хранят переиспользуемые значения. Определяйте их в :root для глобального доступа. Ссылайтесь через var(--name). Они каскадируются и могут быть переопределены в любой области — идеально для темизации (тёмный/светлый режим). В отличие от переменных препроцессора, они динамические (изменяются во время выполнения) и могут управляться через JavaScript. Всегда предоставляйте фолбэки: var(--primary, #007bff). Это современный способ управления дизайн-токенами.

css
: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.

css
/* 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 для адаптивных изображений.

css
/* 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').

css
.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+ уровня), так как она повышает специфичность и снижает поддерживаемость.

css
/* 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;
  }
}
11

Углублённое изучение пользовательских свойств CSS (переменных)

Определение и использование переменных

Пользовательские свойства CSS (переменные) определяются с --name и используются с var(). Определяйте глобальные переменные в :root для тематических значений. В отличие от переменных Sass, CSS-переменные живые — изменение их обновляет все использования мгновенно. Они ограничены областью элемента, на котором определены, и наследуются потомками. var() принимает фолбэк-значение как второй аргумент, и фолбэки могут объединяться в цепочку. Это делает их гораздо более мощными, чем переменные препроцессора, для динамической темизации.

css
: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) переменные во время выполнения, что позволяет создавать динамические палитры цветов, пользовательские настройки и живые превью. Это стандартный подход для тёмного режима, настройки бренда и выбираемых пользователем тем. Переменные каскадируются и наследуются, поэтому переопределение в дочернем элементе затрагивает только это поддерево.

css
/* 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. Этот паттерн мощен для адаптивного дизайна без повторения объявлений свойств.

css
: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) предоставляет значение по умолчанию). Этот подход — основа современных дизайн-систем — измените одну переменную, и вся шкала изменяется пропорционально.

css
: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.

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
  );
}
12

3D-трансформации

Функции 3D-трансформаций

3D-трансформации добавляют элементам глубину. perspective() как функция трансформации применяется к отдельному элементу; свойство perspective на родителе применяется ко всем дочерним элементам равномерно. rotateX/rotateY/rotateZ вращают вокруг осей; translateZ сдвигает вдоль оси Z (к/от зрителя). Меньшие значения perspective создают более драматичные 3D-эффекты (ближняя точка обзора). Всегда устанавливайте perspective на родительский контейнер для согласованного 3D-пространства между несколькими трансформированными дочерними элементами. Комбинируйте несколько трансформаций для сложного 3D-позиционирования.

css
.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-паттернов.

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 на половину размера вашего куба для правильной геометрии.

css
.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 у родителя.

css
.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 анимациями создаёт иммерсивные впечатления от прокрутки.

css
.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); }
13

CSS-фильтры и backdrop-фильтры

Функции фильтров

CSS-фильтры применяют визуальные эффекты к элементам. grayscale, sepia и invert изменяют цвета; blur смягчает; brightness/contrast/saturate регулируют интенсивность; hue-rotate сдвигает цвета. Несколько фильтров объединяются слева направо. drop-shadow превосходит box-shadow для PNG/SVG-изображений, так как следует форме альфа-канала (прозрачные области не затеняются). Фильтры ускоряются GPU и работают хорошо. Частое использование: grayscale при наведении для галерей изображений, винтажные фотоэффекты и доступность (режимы высокой контрастности).

css
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).

css
.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: жидкое морфирование, смещение, освещение и пользовательское смешивание.

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, чтобы подсказать браузеру оптимизацию.

css
@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 создаёт новый контекст смешивания для ограничения эффектов смешивания.

css
/* 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;
}
14

Container queries

Основы container queries

Container queries позволяют компонентам реагировать на размер контейнера, а не viewport. container-type: inline-size делает элемент контейнером запросов на основе его inline-размерности (ширины). Правило @container применяет стили, когда контейнер удовлетворяет условию. Это революционно для компонентного дизайна — карточка адаптируется, будь то в боковой панели или полноширинной основной области, независимо от размера экрана. Это решает фундаментальное ограничение media queries, которые реагируют только на размер viewport.

css
/* 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 запрашивает ближайший родительский контейнер.

css
/* 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 (обе размерности).

css
.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.

css
/* 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 для типографики даёт полностью сам адаптирующиеся компоненты.

css
/* 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;
  }
}
15

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 для более мягкого поведения.

css
/* 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 для анимированных переходов между привязками.

css
.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+, рекомендуется прогрессивное улучшение для других браузеров.

css
/* 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.

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' контролирует вертикальное выравнивание цели.

css
/* 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; }
}
16

CSS-счётчики

Базовый счётчик

CSS-счётчики автоматически нумеруют элементы без JavaScript. counter-reset инициализирует счётчик (на родителе или любом предке). counter-increment увеличивает его (обычно на нумеруемом элементе). counter(name) отображает текущее значение в content. Счётчик автоматически обрабатывает нумерацию по всему документу. Идеально для нумерации заголовков, элементов списка, рисунков или любого последовательного контента. Счётчики ограничены областью элемента, где они сброшены, и его потомками.

css
/* 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 и точек сброса.

css
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 браузерами хорошая в современных браузерах.

css
/* 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() для нумерации в стиле плана вложенных списков, оглавлений или иерархических меню. Строка-разделитель может быть любым символом (. , - > и т. д.).

css
/* 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-якоря. Но для отображаемой нумерации это чистое, не требующее обслуживания решение.

css
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;
}
18

Тёмный режим и цветовые схемы

prefers-color-scheme

prefers-color-scheme: dark обнаруживает предпочтение тёмного режима ОС/браузера пользователя. Определите переменные темы в :root для светлого режима, затем переопределите их в media query для тёмного. Использование CSS-переменных означает, что меняются только значения переменных — все компоненты обновляются автоматически. Это стандартный подход для тёмного режима. Media query также поддерживает 'light' и 'no-preference'. Тестируйте, переключая тёмный режим ОС или используя вкладку Rendering в Chrome DevTools (Emulate CSS prefers-color-scheme).

css
/* 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)') как фолбэк.

css
/* 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-переменных — влияет только на нативный рендеринг браузера.

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. Всегда тестируйте изображения в обоих режимах на читаемость.

css
@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).

css
/* 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; }
}
19

Углублённое изучение функций CSS

clamp(), min(), max()

clamp(min, preferred, max) — священный Грааль жидкой типографики и интервалов — масштабируется с предпочтительным значением, но никогда не выходит за границы min/max. min() выбирает меньшее значение (отлично для адаптивных max-width). max() выбирает большее (отлично для минимальных размеров). Они устраняют многие media queries, обеспечивая непрерывное жидкое масштабирование. Предпочтительное значение в clamp обычно использует viewport-единицы (vw, vh) или единицы контейнера (cqw). Это современный подход к адаптивному дизайну без брейкпоинтов.

css
/* 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-переменными позволяет динамические вычисляемые дизайн-системы. Необходимо для адаптивных макетов, сочетающих фиксированные и жидкие размерности.

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).

css
/* 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() и другие функции. Используйте фолбэки для опциональных тематических переменных, которые могут быть не определены во всех контекстах.

css
/* 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. Градиенты независимы от разрешения и работают лучше, чем файлы изображений.

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);
}
20

CSS-переменные

Определение и использование переменных

Пользовательские свойства CSS (переменные) определяются с --name и используются с var(). Объявляйте их на :root для глобального доступа. В отличие от переменных Sass, они живые — изменение переменной через JavaScript мгновенно обновляет каждый элемент, использующий её.

css
:root {
  --primary: #3498db;
  --spacing: 16px;
  --radius: 8px;
}

.button {
  background: var(--primary);
  padding: var(--spacing);
  border-radius: var(--radius);
}

Значения фолбэка

var() принимает второй аргумент как фолбэк, когда переменная не определена. Фолбэки можно вкладывать, позволяя объединять значения по умолчанию в цепочку. Для браузеров, не поддерживающих пользовательские свойства, объявите обычное значение сначала, затем переопределите с var().

css
.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(). Это делает темизацию во время выполнения тривиальной — переключите несколько переменных для рестайлинга всего приложения.

css
// 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 переопределяет только для этого поддерева. Это ограничение области видимости позволяет темизацию на уровне компонента без войн специфичности.

css
: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, чтобы весь макет реагировал на брейкпоинты без переписывания правил. Измените одну переменную, и каждый использующий её элемент адаптируется — намного чище, чем переопределять каждое свойство отдельно.

css
: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); }
21

Анимации и ключевые кадры

@keyframes и сокращение animation

@keyframes определяет начальное (from/0%) и конечное (to/100%) состояния анимации. Сокращение animation объединяет name, duration, timing-function, delay, iteration-count, direction, fill-mode и play-state. forwards сохраняет конечное состояние.

css
@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 удобно, но может повредить производительности — предпочитайте перечисление конкретных свойств.

css
.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() создаёт дискретный эффект.

css
/* 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 для цепочки анимаций.

css
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 умеренно для подсказки браузеру.

css
/* 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; }
22

3D-трансформации

2D-трансформации

2D-трансформации перемещают, вращают, масштабируют и искажают элементы, не затрагивая окружающий макет. Трансформации композируются на GPU, что делает их производительными для анимации. Порядок важен: rotate затем translate двигает вдоль повёрнутой оси.

css
.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.

css
.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, поэтому отображается при перевороте контейнера.

css
.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 куба для вращения.

css
.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-ускорение, улучшая плавность анимации. Избегайте чрезмерного использования слоёв.

css
/* 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); }
23

Продвинутый CSS Grid

Области шаблона Grid

grid-template-areas именует ячейки сетки визуально, делая намерение макета очевидным. Каждая строка в кавычках представляет ряд сетки; одинаковые имена охватывают ячейки. Пустые ячейки используют точку (.). Области должны образовывать прямоугольники — L-формы недопустимы.

css
.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 оставляет их зарезервированными.

css
.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 — сокращение для обоих.

css
.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 заставляет элемент охватывать всю ширину.

css
.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 года.

css
.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);
}
24

Функции CSS

calc()

calc() выполняет математические операции со смешанными единицами (px, %, vw, em, rem). Всегда окружайте + и - пробелами (calc(100%-20px) не работает; calc(100% - 20px) работает). Умножение и деление требуют, чтобы одна сторона была числом без единиц.

css
.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() ограничивает значение между минимумом и максимумом с предпочтительным значением посередине. Это самый чистый способ построить жидкую типографику и интервалы — значение масштабируется, но никогда не выходит ниже или выше границ.

css
/* 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() — снизу.

css
/* 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() для цепочки фолбэков.

css
.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) позволяет регулировать отдельные каналы.

css
/* 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); }
25

Scroll-driven анимации

Основы animation-timeline

animation-timeline: scroll(root) привязывает анимацию к позиции прокрутки страницы — по мере прокрутки пользователем анимация прогрессирует. JavaScript не требуется. Это намного плавнее, чем слушатели событий прокрутки.

css
@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.

css
@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% заставляет полосу расти слева.

css
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).

css
.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% охватывает только когда элемент полностью внутри.

css
.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%;
}
26

CSS-слои

Определение @layer

@layer группирует стили в именованные слои каскада. Порядок в строке объявления задаёт приоритет: более поздние слои побеждают более ранние, независимо от специфичности селектора. Это приносит предсказуемое упорядочивание в большие таблицы стилей.

css
@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. Неслоевые стили имеют приоритет над всеми слоевыми стилями.

css
@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, и ваши пользовательские стили (в более позднем слое или неслойные) всегда победят.

css
@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.

css
@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, поэтому принадлежность к слою адаптируется к условиям. Это удерживает специфичные для функций стили организованными внутри их слоя, а не утекает в неслойное пространство.

css
@layer base, components;

@media (max-width: 600px) {
  @layer components {
    .nav { flex-direction: column; }
  }
}

@supports (display: grid) {
  @layer components {
    .layout { display: grid; }
  }
}
27

Backdrop-фильтры

Основы backdrop-filter

backdrop-filter применяет фильтр к контенту позади элемента, создавая эффекты матового стекла. Всегда включайте префикс -webkit- для Safari. Комбинируйте с полупрозрачным фоном, чтобы размытие было видно.

css
.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. Каждый применяется к фону. Сочетайте с тинтным фоном для управления настроением.

css
.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 инвертирует. Будьте осторожны с читаемостью текста.

css
.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 — может вызывать серьёзные рывки.

css
/* 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 усиливает цвета, видимые сквозь стекло.

css
.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);
}

Was this helpful?

Learning path

Learn from scratch

Learn this language from the ground up with structured lessons.