diff --git a/src/pages/css/index.md b/src/pages/css/index.md index 175b67b..082e3a9 100644 --- a/src/pages/css/index.md +++ b/src/pages/css/index.md @@ -22,8 +22,48 @@ cascade и inheritance, вычисляет значения, а затем ис **Полный ответ** -CSS описывает presentation документа. Браузер разбирает stylesheets в CSSOM, сопоставляет selectors с DOM, разрешает -cascade и inheritance, вычисляет значения, а затем использует их для layout, paint и compositing. +CSS задает правила presentation для DOM: какие элементы участвуют в layout, какие у них размеры, цвета, шрифты, эффекты +и как эти значения меняются в разных состояниях. + +Упрощенно browser проходит несколько этапов: + +1. разбирает stylesheets и строит CSSOM; +2. находит rules, selectors которых подходят DOM elements; +3. для каждого property разрешает cascade; +4. применяет inheritance и CSS-wide values; +5. получает computed values; +6. использует результат при построении layout, paint и compositing. + +Например: + +```css +.button { + color: white; + background: royalblue; + padding: 0.75rem 1rem; +} +``` + +Selector `.button` определяет область применения rule, а declarations становятся кандидатами на значения конкретных +properties. Если другой rule тоже задает `color`, browser не просто берет «последний CSS»: сначала учитываются cascade +origin, importance/layer, specificity и другие этапы cascade. + +CSS parsing устойчив к ошибкам. Если browser не понимает отдельную declaration, например неизвестное property или +невалидное value, он обычно игнорирует ее и продолжает разбирать stylesheet. Это позволяет писать progressive fallback: + +```css +.card { + background: rgb(20 20 20); + background: color(display-p3 0.1 0.1 0.1); +} +``` + +Если DOM, class, media/container condition или используемая custom property меняется, browser может выполнить style +recalculation. Но изменение CSS не означает автоматически полный layout: `color` в основном влияет на paint, а `width` +может потребовать layout; `transform` часто можно обработать на compositing stage. + +На интервью полезно связать CSS с rendering pipeline: **selector matching и cascade дают computed styles, после чего +конкретные properties определяют, понадобится ли layout, paint или только compositing**. @@ -35,13 +75,65 @@ cascade и inheritance, вычисляет значения, а затем ис **Короткий ответ** -Selector выбирает элементы, declaration задает пару property/value внутри rule. Несколько selectors могут совпасть с -одним элементом, после чего cascade определяет победившее значение каждого property. +Selector выбирает элементы, declaration задает пару `property: value` внутри rule. Несколько rules могут задавать одно +property одному элементу, после чего cascade выбирает итоговое значение. **Полный ответ** -Selector выбирает элементы, declaration задает пару property/value внутри rule. Несколько selectors могут совпасть с -одним элементом, после чего cascade определяет победившее значение каждого property. +CSS rule обычно состоит из **selector** и блока **declarations**: + +```css +.card > .title { + color: darkslateblue; + font-weight: 600; +} +``` + +`.card > .title` — selector. Он описывает, каким DOM elements подходит правило. + +Внутри блока две declarations: + +```text +color: darkslateblue +font-weight: 600 +``` + +У каждой declaration есть property и value. Browser рассматривает конфликт **по каждому property отдельно**: один rule +может победить для `color`, а другое подходящее правило — для `font-size`. + +Selectors бывают разного типа: + +```css +button { +} /* type selector */ +.action { +} /* class selector */ +[aria-expanded='true'] { +} /* attribute selector */ +.card > .title { +} /* combinators */ +.button:hover { +} /* pseudo-class */ +.button::before { +} /* pseudo-element */ +``` + +Shorthand declaration может задавать сразу несколько longhand properties: + +```css +.card { + margin: 1rem 2rem; +} +``` + +Она влияет на `margin-top`, `margin-right`, `margin-bottom` и `margin-left`, поэтому более поздний longhand способен +переопределить только одну сторону. + +Важно не связывать selector с application state сильнее необходимого. Например, `.sidebar > ul > li > button` сильно +зависит от DOM structure, а `.sidebar-action` переживет дополнительный wrapper гораздо легче. + +На интервью: **selector отвечает «кому», declaration — «какое property/value», а cascade разрешает конфликты между +подходящими declarations независимо для каждого property**. @@ -53,13 +145,61 @@ Selector выбирает элементы, declaration задает пару pr **Короткий ответ** -Cascade разрешает конфликт declarations по relevance, origin и importance, cascade layer, specificity, scope proximity и -порядку. Specificity — только один этап, поэтому «самый тяжелый selector всегда побеждает» — неверное упрощение. +Cascade разрешает конфликт declarations по relevance, origin/importance, cascade layer, specificity, scope proximity и +порядку объявления. Specificity — только один из этапов. **Полный ответ** -Cascade разрешает конфликт declarations по relevance, origin и importance, cascade layer, specificity, scope proximity и -порядку. Specificity — только один этап, поэтому «самый тяжелый selector всегда побеждает» — неверное упрощение. +Cascade отвечает на вопрос: **какая declaration победит, если одному element подходят несколько значений одного +property?** + +Упрощенный порядок принятия решения: + +1. **Relevance** — rule вообще должен быть активен, например совпадает ли `@media` condition. +2. **Origin и importance** — user-agent, user и author styles, normal/`!important`, animations/transitions. +3. **Cascade layers** — внутри origin учитывается порядок `@layer`. +4. **Specificity** — сравнивается вес selectors, которые еще остаются кандидатами. +5. **Scoping proximity** — для конфликтующих `@scope` rules ближний scope root может получить приоритет. +6. **Order of appearance** — если предыдущие условия равны, побеждает более поздняя declaration. + +Пример layers: + +```css +@layer reset, components, overrides; + +@layer components { + .button { + color: blue; + } +} + +@layer overrides { + .button { + color: purple; + } +} +``` + +Для обычных declarations более поздний layer имеет больший приоритет, поэтому `purple` победит без увеличения +specificity. Это позволяет управлять архитектурой cascade явно вместо гонки selectors. + +Важный нюанс: `!important` не «добавляет specificity». Сначала declaration попадает в другой importance bucket, и уже +внутри конкурирующих important declarations применяются остальные правила. Порядок layers для important declarations +также инвертируется, чтобы ранние защитные layers нельзя было случайно перебить поздними overrides. + +Поэтому такой подход хрупок: + +```css +#app .page .form .button.primary { + color: red !important; +} +``` + +Он создает escalation: следующему разработчику приходится писать еще более сильный selector или новый `!important`. Чаще +лучше уменьшить specificity, использовать component boundary или cascade layers. + +На интервью сильный ответ: **specificity не равна cascade; сначала browser определяет cascade bucket/layer, затем +сравнивает specificity, scope proximity и только потом source order**. @@ -71,13 +211,75 @@ Cascade разрешает конфликт declarations по relevance, origin **Короткий ответ** -Некоторые properties, например color и font-family, по умолчанию наследуют computed value родителя; размеры и box -properties обычно нет. Наследование применяется после cascade, когда для элемента нет собственного выигравшего значения. +Некоторые properties, например `color` и `font-family`, по умолчанию наследуют computed value родителя; box/layout +properties обычно нет. `inherit` позволяет запросить наследование явно. **Полный ответ** -Некоторые properties, например `color` и `font-family`, по умолчанию наследуют computed value родителя; размеры и box -properties обычно нет. Наследование применяется после cascade, когда для элемента нет собственного выигравшего значения. +Inheritance позволяет descendant element получить значение property от parent, если у него нет собственного результата +cascade и само property является inherited. + +Типичные inherited properties относятся к тексту: + +```css +body { + color: #222; + font-family: system-ui; +} +``` + +Большая часть текста внутри `body` автоматически получит эти значения без отдельного rule для каждого element. + +Layout properties обычно не наследуются: + +```css +.parent { + margin: 2rem; + border: 1px solid; +} +``` + +Child не получает `margin` и `border`, иначе layout быстро стал бы непредсказуемым. + +Наследуется не source text declaration, а **computed value** родителя. Это особенно заметно с relative values: + +```css +.parent { + color: currentColor; +} +``` + +и custom properties, которые по умолчанию тоже наследуются: + +```css +.theme-dark { + --surface: #111; +} + +.card { + background: var(--surface); +} +``` + +Наследование можно включить явно даже для non-inherited property: + +```css +.child { + border-color: inherit; +} +``` + +или сбросить через `initial`, `unset`, `revert` и другие CSS-wide keywords. + +Важно не путать inheritance с descendant selectors. Rule `.parent .child` применяется из-за selector matching, а не +потому, что CSS property «унаследовалось». + +Практически inheritance полезно использовать для typography, color и design tokens, но опасно строить на нем скрытые +component contracts. Если component работает только потому, что где-то далеко ancestor задает случайную custom property, +его reuse становится сложнее. + +На интервью: **inheritance — это передача computed value по DOM parent-child relation для определенных properties, а не +механизм выбора selector**. @@ -89,15 +291,78 @@ properties обычно нет. Наследование применяется **Короткий ответ** -Каждое property имеет initial value. inherit берет значение родителя, initial возвращает specification default, unset -выбирает inherit или initial по природе property, revert откатывает текущий cascade origin/layer к более раннему -результату. +Каждое property имеет specification initial value. `inherit` берет значение parent, `initial` возвращает specification +default, `unset` выбирает `inherit` или `initial` по природе property, `revert` откатывает текущий cascade origin. Для +отката текущего layer есть `revert-layer`. **Полный ответ** -Каждое property имеет initial value. `inherit` берет значение родителя, `initial` возвращает specification default, -`unset` выбирает inherit или initial по природе property, `revert` откатывает текущий cascade origin/layer к более -раннему результату. +У каждого CSS property есть **initial value**, определенное спецификацией. Это не обязательно то, что browser визуально +показывает элементу по умолчанию, потому что поверх initial values работают user-agent styles. + +Например: + +```css +div { + display: initial; +} +``` + +Initial value для `display` — `inline`, поэтому это не то же самое, что browser default `display: block` для `div`. + +CSS-wide keywords решают разные задачи. + +**`inherit`** — взять computed value parent независимо от того, наследуется ли property обычно: + +```css +.child { + border-color: inherit; +} +``` + +**`initial`** — использовать specification initial value: + +```css +.element { + color: initial; +} +``` + +**`unset`** — вести себя как `inherit` для inherited property и как `initial` для остальных: + +```css +.component { + all: unset; +} +``` + +`all: unset` выглядит как удобный reset, но может убрать display, interaction-related styles и ожидаемые browser +defaults, поэтому использовать его нужно осознанно. + +**`revert`** — убрать влияние declarations текущего cascade origin и вернуться к результату предыдущего origin. Для +author styles это часто позволяет снова увидеть user/user-agent behavior: + +```css +button { + font: revert; +} +``` + +**`revert-layer`** — более локальный вариант: игнорирует declaration текущего cascade layer и ищет значение в предыдущих +layers того же origin. + +```css +@layer base, components; + +@layer components { + .special { + color: revert-layer; + } +} +``` + +На интервью важно не говорить «initial возвращает browser default». **Initial — значение из specification, revert — +возврат по cascade origin, revert-layer — возврат по layer**. @@ -109,13 +374,52 @@ properties обычно нет. Наследование применяется **Короткий ответ** -Box состоит из content, padding, border и margin. При content-box заданная ширина относится только к content, а при -border-box включает padding и border. Margin находится снаружи и не входит в размер border box. +Box состоит из content, padding, border и margin. При `content-box` `width`/`height` относятся к content, при +`border-box` включают padding и border. Margin находится снаружи border box. **Полный ответ** -Box состоит из content, padding, border и margin. При `content-box` заданная ширина относится только к content, а при -`border-box` включает padding и border. Margin находится снаружи и не входит в размер border box. +Большинство visual elements browser представляет как boxes. Классическая box model состоит из четырех областей: + +```text +margin + border + padding + content +``` + +При default `box-sizing: content-box`: + +```css +.card { + width: 200px; + padding: 20px; + border: 2px solid; +} +``` + +`200px` относится только к content box. Фактическая ширина border box будет: + +```text +200 + 20 + 20 + 2 + 2 = 244px +``` + +При `box-sizing: border-box` те же `width: 200px` уже включают content + padding + border, поэтому внешний размер +предсказуемее. + +Margin находится за border и не входит в `width` border box. У vertical margins обычных block elements в normal flow +есть еще один важный edge case — **margin collapsing**: соседние vertical margins могут схлопываться вместо простого +сложения. Во Flexbox/Grid такого классического collapsing между items нет. + +Не все visual effects входят в box model. Например, `outline` и `box-shadow` могут рисоваться за border box, но обычно +не увеличивают layout size элемента. + +Sizing также ограничивают `min-width`, `max-width`, intrinsic sizes и правила конкретного layout mode. Поэтому +`width: 100%` не всегда означает «ровно ширина родителя» — padding, box-sizing, min-content constraints и containing +block тоже имеют значение. + +На интервью: **box model объясняет, из каких областей складывается layout size; затем нужно связать ее с `box-sizing`, +margin collapsing и тем, что paint effects не обязательно участвуют в layout**. @@ -127,13 +431,64 @@ Box состоит из content, padding, border и margin. При `content-box` **Короткий ответ** -rem зависит от root font size, em — от font size текущего контекста, % — от property-specific containing value, viewport -units — от viewport, px — CSS pixel. Выбор определяется тем, относительно чего размер должен изменяться. +`rem` зависит от root font size, `em` — от font size контекста, `%` — от правила конкретного property, viewport units — +от viewport, `px` — CSS pixel. Единицу выбирают по тому, относительно чего значение должно масштабироваться. **Полный ответ** -`rem` зависит от root font size, `em` — от font size текущего контекста, `%` — от property-specific containing value, -viewport units — от viewport, `px` — CSS pixel. Выбор определяется тем, относительно чего размер должен изменяться. +Универсально «лучшей» CSS unit нет. Вопрос в том, **какую зависимость мы хотим выразить**. + +**`px`** — CSS pixel, логическая единица browser, а не гарантированно один physical pixel экрана. Удобен для значений, +которые не должны зависеть от typography: например, тонкий border или конкретный minimum size. + +**`rem`** зависит от computed `font-size` root element (`html`): + +```css +.card { + padding: 1rem; +} +``` + +Это удобно для глобальной typography/spacing scale, которая реагирует на root font size. + +**`em`** зависит от font size текущего контекста. Для `font-size` самого element reference берется из parent font size, +а для большинства других properties — из computed font size самого element: + +```css +.badge { + font-size: 0.875rem; + padding-inline: 0.75em; +} +``` + +Так padding badge масштабируется вместе с его текстом. + +**`%`** зависит от property. Например, `width: 50%` обычно связан с containing block, а percentage в `transform` может +относиться к box самого transformed element. Поэтому `%` нельзя объяснить одной фразой «процент от родителя». + +**Viewport units**: + +```css +.hero { + min-height: 100dvh; +} +``` + +`vw`/`vh` относятся к viewport, но на mobile классический `100vh` исторически неудобен из-за browser chrome. Современные +`svh`, `lvh`, `dvh` различают small, large и dynamic viewport size и позволяют выбрать нужное поведение. + +Практический выбор часто выглядит так: + +- typography и scalable spacing — `rem`; +- размеры, связанные с локальным text — `em`; +- fluid layout — `%`, `fr`, container/viewport units; +- точные технические ограничения — иногда `px`. + +Не стоит запрещать `px` догматически. Accessibility определяется тем, может ли интерфейс zoom/reflow и уважает ли +пользовательские настройки, а не самим наличием символов `px` в stylesheet. + +На интервью: **unit — это dependency. Сильный ответ объясняет reference value каждой единицы и выбирает ее из desired +responsive behavior**. @@ -145,13 +500,90 @@ viewport units — от viewport, `px` — CSS pixel. Выбор определ **Короткий ответ** -Custom properties объявляются как --color и читаются через var(). Они участвуют в cascade, наследуются и могут меняться -runtime. Sass variables вычисляются при сборке и не существуют в browser CSSOM после compilation. +Custom properties объявляются как `--token` и используются через `var()`. Они участвуют в cascade, по умолчанию +наследуются и вычисляются runtime; `@property` может задать тип, initial value и управление inheritance. **Полный ответ** -Custom properties объявляются как `--color` и читаются через `var()`. Они участвуют в cascade, наследуются и могут -меняться runtime. Sass variables вычисляются при сборке и не существуют в browser CSSOM после compilation. +CSS custom property — настоящее CSS property с именем, начинающимся на `--`: + +```css +:root { + --color-accent: #5b5bd6; +} + +.button { + background: var(--color-accent); +} +``` + +В отличие от Sass variable, custom property существует **в browser runtime**. Поэтому она участвует в cascade и +inheritance, может меняться под class/media condition и через DOM API. + +Это делает custom properties удобной основой themes/design tokens: + +```css +:root { + --surface: white; + --text: #222; +} + +[data-theme='dark'] { + --surface: #151515; + --text: #f5f5f5; +} + +.card { + color: var(--text); + background: var(--surface); +} +``` + +У `var()` есть fallback: + +```css +.card { + color: var(--card-color, currentColor); +} +``` + +Fallback используется, когда custom property недоступна/invalid для substitution, а не как polyfill для browsers без +поддержки custom properties. + +Есть важный edge case: обычная `--custom-property` принимает почти произвольный token stream. Ошибка может проявиться +только **at computed-value time**: + +```css +:root { + --theme-color: 45deg; +} + +.card { + background-color: var(--theme-color); +} +``` + +`--theme-color` сама по себе syntactically допустима, но после substitution `45deg` не является цветом. Итоговое +`background-color` станет invalid at computed-value time и откатится по правилам property, что иногда дает неожиданный +результат. + +`@property` позволяет зарегистрировать typed custom property: + +```css +@property --progress { + syntax: ''; + inherits: false; + initial-value: 0; +} +``` + +Это дает validation, initial value, контроль inheritance и более предсказуемую animation/interpolation. + +Ограничение: `var()` подставляет values, но не может динамически создавать property names, selectors или conditions в +`@media`/container query. + +На интервью: **custom properties — часть cascade/runtime model CSS, а не просто текстовая замена переменных; нужно +упомянуть inheritance, fallback и `@property`**. @@ -163,13 +595,63 @@ Custom properties объявляются как `--color` и читаются ч **Короткий ответ** -currentColor означает вычисленное значение property color. Его удобно использовать для borders, shadows и SVG иконок, -чтобы они автоматически следовали цвету текста и состояниям компонента. +`currentColor` означает computed значение property `color`. Его используют в borders, SVG, shadows и других color +positions, чтобы visual parts автоматически следовали цвету текста/состоянию компонента. **Полный ответ** -`currentColor` означает вычисленное значение property `color`. Его удобно использовать для borders, shadows и SVG -иконок, чтобы они автоматически следовали цвету текста и состояниям компонента. +`currentColor` — CSS keyword, который подставляет текущее computed значение `color` элемента. + +Например: + +```css +.link { + color: royalblue; + border-bottom: 1px solid currentColor; +} +``` + +При `:hover` достаточно изменить одно property: + +```css +.link:hover { + color: darkblue; +} +``` + +Border автоматически станет `darkblue`, потому что его цвет связан с `currentColor`. + +Особенно полезен этот pattern для monochrome SVG icons: + +```html + +``` + +```css +.icon-button { + color: var(--icon-color); +} +``` + +Так icon следует hover/disabled/theme state без отдельного asset или selector для внутреннего `path`. + +Некоторые properties и так используют `currentColor` как initial color behavior, например border colors. Явная запись +все равно может быть полезна как documentation intent в component API. + +Trade-off: `currentColor` связывает два визуальных канала. Для multi-color illustration или border, который должен иметь +независимый semantic token, такая связь уже не подходит. + +На интервью: **currentColor позволяет выразить dependency «этот цвет такой же, как text color» и уменьшает количество +синхронизируемых state rules**. @@ -181,33 +663,70 @@ currentColor означает вычисленное значение property c **Короткий ответ** -Специфичность селектора записывают как три числа: ID - классы - типы. +Specificity selector обычно сравнивают как `ID - class/attribute/pseudo-class - type/pseudo-element`. `!important`, +origin и cascade layer не являются частью specificity и обрабатываются раньше в cascade. **Полный ответ** -Специфичность селектора записывают как три числа: `ID - классы - типы`. +Specificity — один из tie-breakers cascade. Ее удобно представлять тремя группами: + +```text +ID | CLASS | TYPE +``` -- универсальный селектор `*`: `0-0-0`; -- селектор типа `button`: `0-0-1`; -- class, attribute и pseudo-class: `0-1-0`; -- ID selector: `1-0-0`. +Примеры: ```css +* { +} /* 0-0-0 */ button { -} - +} /* 0-0-1 */ .button { +} /* 0-1-0 */ +[type='button'] { +} /* 0-1-0 */ +.button:hover { +} /* 0-2-0 */ +#checkout .button { +} /* 1-1-0 */ +``` + +Если competing declarations уже находятся в одном cascade bucket/layer, более высокая specificity побеждает. При равной +specificity дальше учитываются scope proximity и source order. + +Есть важные modern pseudo-class rules. + +**`:where()` имеет нулевую specificity:** + +```css +:where(.card .title) { + margin: 0; } +``` + +Это удобно для defaults, которые consumers должны легко переопределять. + +**`:is()`, `:not()` и `:has()` сами не добавляют обычный class-weight; их specificity определяется наиболее специфичным +selector из переданного списка:** -#button { +```css +:is(.button, #critical) { + /* specificity учитывает #critical */ } ``` -Если все три селектора подходят одному элементу, победит `#button` со специфичностью `1-0-0`, затем `.button` с `0-1-0`, -затем `button` с `0-0-1`. При одинаковой специфичности выигрывает правило, расположенное позже в cascade. +Поэтому случайный ID внутри `:is()` способен неожиданно усилить rule. + +Inline style имеет отдельный высокий author priority относительно обычных selector rules. `!important` тоже не является +«четвертой цифрой» specificity: он переводит declaration в important cascade, где затем снова сравниваются layers, +specificity и остальные criteria. + +Практическая проблема — specificity escalation. Если component требует selectors вроде `#app .page .dialog .button`, +переопределения становятся дорогими. Cascade layers, low-specificity defaults через `:where()` и component boundaries +обычно масштабируются лучше. -Inline style рассматривают отдельно: он сильнее обычной специфичности selector. `!important` тоже не является частью -специфичности: он меняет приоритет declaration в cascade, после чего сравниваются origin, layer, specificity и порядок. +На интервью: **specificity нужно уметь считать, но еще важнее сказать, где она находится внутри cascade и почему +`!important`/layers нельзя смешивать с ее весом**. ![img.png](assets/css-specificity.png) @@ -221,11 +740,13 @@ Inline style рассматривают отдельно: он сильнее о **Короткий ответ** -То есть CSS, который браузер сам применяет к HTML-элементам, даже если ты не написал свой CSS. +User agent stylesheet — встроенные browser styles для HTML elements. Благодаря им headings, links, buttons, inputs и +другие native elements имеют базовый вид и behavior даже без author CSS. **Полный ответ** -То есть CSS, который браузер сам применяет к HTML-элементам, даже если ты не написал свой CSS. +Browser поставляет собственный **user agent stylesheet**. Поэтому страница без единой author declaration все равно не +выглядит как неформатированный текст: ```html

Hello

@@ -233,21 +754,40 @@ Inline style рассматривают отдельно: он сильнее о ``` -Даже без CSS у них уже есть внешний вид: +Browser обычно задает heading размер/weight/margins, link — color/decoration, button/input — platform form appearance. +Конкретные defaults могут отличаться между browser/OS. + +Упрощенный пример UA rules: ```css h1 { + display: block; font-size: 2em; font-weight: bold; - margin-block-start: 0.67em; - margin-block-end: 0.67em; } +``` -button { - appearance: auto; +Author styles в обычной ситуации переопределяют normal UA declarations через cascade: + +```css +h1 { + margin: 0; } ``` +Но важно различать **UA default и specification initial value**. Например, `display: initial` не означает «верни обычный +display этого HTML tag»: initial value `display` — `inline`, тогда как UA stylesheet делает `div` block. Для возврата к +более раннему cascade origin чаще подходит `revert`. + +UA styles также несут полезное platform behavior: focus outlines, form control appearance, disabled states. Aggressive +reset вида `button { all: unset; }` может удалить важные affordances, которые потом придется восстанавливать вручную. + +В DevTools user agent rules обычно видны рядом с author styles, поэтому при странном margin/input appearance стоит +сначала проверить cascade, а не считать это «магией браузера». + +На интервью: **UA stylesheet — самый базовый style origin browser; reset/normalize взаимодействуют именно с этими +defaults, а не меняют HTML semantics**. + @@ -258,11 +798,37 @@ button { **Короткий ответ** -При border-box заданные width и height уже включают padding и border. Это делает размеры элементов предсказуемее. +При `box-sizing: border-box` заданные `width` и `height` включают content, padding и border. Это делает внешние размеры +компонента предсказуемее. **Полный ответ** -При `border-box` заданные `width` и `height` уже включают padding и border. Это делает размеры элементов предсказуемее. +Default `box-sizing` для большинства elements — `content-box`. Поэтому: + +```css +.card { + width: 300px; + padding: 20px; + border: 2px solid; +} +``` + +дает border box шириной `344px`. + +С `border-box`: + +```css +.card { + box-sizing: border-box; + width: 300px; + padding: 20px; + border: 2px solid; +} +``` + +внешняя ширина до margin остается `300px`, а content area уменьшается, чтобы освободить место padding/border. + +Поэтому многие projects задают global baseline: ```css *, @@ -272,6 +838,30 @@ button { } ``` +или наследуемый вариант: + +```css +html { + box-sizing: border-box; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} +``` + +Второй pattern позволяет отдельному subtree при необходимости сменить sizing model и передать ее descendants. + +`border-box` не означает, что element всегда физически поместится в parent. `min-width`, intrinsic content, long +unbreakable text, grid/flex sizing и margin все равно могут создать overflow. + +Также `margin` никогда не включается в `border-box`, а `outline`/shadow не участвуют в вычислении declared width. + +На интервью: **border-box меняет интерпретацию declared width/height, но не отменяет остальные правила intrinsic и +layout sizing**. + @@ -282,17 +872,55 @@ button { **Короткий ответ** -Браузер строит CSSOM и сопоставляет selectors с DOM-элементами, обычно начиная с правой части selector и проверяя -ancestors дальше влево. Поэтому слишком общие правые части и глубокие chains могут быть дороже, но в большинстве -интерфейсов bottleneck чаще в layout, paint и количестве DOM, а не в одном selector. Писать selectors все равно стоит -простыми и устойчивыми к изменениям разметки. +Browser engines оптимизируют selector matching и концептуально проверяют selector от правой части к ancestors. Глубокие +или слишком общие selectors могут увеличить работу, но чаще реальные CSS bottlenecks связаны со style invalidation, +layout/paint и размером DOM. **Полный ответ** -Браузер строит CSSOM и сопоставляет selectors с DOM-элементами, обычно начиная с правой части selector и проверяя -ancestors дальше влево. Поэтому слишком общие правые части и глубокие chains могут быть дороже, но в большинстве -интерфейсов bottleneck чаще в layout, paint и количестве DOM, а не в одном selector. Писать selectors все равно стоит -простыми и устойчивыми к изменениям разметки. +Для rule: + +```css +.sidebar .menu > li.active a { + color: red; +} +``` + +browser должен найти elements, которые удовлетворяют всей цепочке relationships. Удобная mental model — matching идет +справа налево: сначала рассматривается `a`, затем проверяются нужные ancestors/siblings/conditions слева. + +Но современные engines используют индексы, bloom filters, caches и другие оптимизации, поэтому правило «любой длинный +selector медленный» слишком грубое. На обычной странице замена `.list .item` на `.item` редко даст заметный performance +win сама по себе. + +Важнее **style invalidation**. Когда class/attribute/DOM relation меняется, browser должен понять, какие elements могли +потерять или получить matching rules и для каких computed styles нужен пересчет. + +Например relational selectors вроде `:has()` позволяют parent зависеть от descendants: + +```css +.card:has(.error) { + border-color: red; +} +``` + +Engines оптимизируют и такие cases, но dependency graph становится шире, поэтому на очень больших/часто меняющихся trees +важно измерять реальные invalidation costs, а не полагаться на intuition. + +Еще одна причина избегать глубоких selectors — maintainability: + +```css +.page > .sidebar > ul > li > a { +} +``` + +сломается от дополнительного wrapper гораздо раньше, чем простой component class. + +Для performance investigation полезнее смотреть DevTools Performance trace: время Recalculate Style, Layout, Paint, +размер DOM и frequency mutations. Selector micro-benchmark без реального scenario часто оптимизирует не тот bottleneck. + +На интервью: **matching — только часть style calculation; архитектурно важны простые selectors, а performance решается +через measurement style invalidation + rendering pipeline**. @@ -304,19 +932,70 @@ ancestors дальше влево. Поэтому слишком общие пр **Короткий ответ** -Reset CSS aggressively сбрасывает browser defaults, чтобы начать с почти пустой базы. Normalize CSS сохраняет полезные -defaults и выравнивает различия между браузерами точечнее. В современных проектах часто используют небольшой base layer: -box-sizing, typography, form controls и явно выбранные defaults дизайн-системы. +Reset CSS aggressively убирает browser defaults, Normalize сохраняет полезные defaults и точечно выравнивает browser +различия. Современный project часто использует небольшой собственный base layer вместо полного универсального reset. **Полный ответ** -Reset CSS aggressively сбрасывает browser defaults, чтобы начать с почти пустой базы. Normalize CSS сохраняет полезные -defaults и выравнивает различия между браузерами точечнее. В современных проектах часто используют небольшой base layer: -`box-sizing`, typography, form controls и явно выбранные defaults дизайн-системы. +Оба подхода пытаются сделать стартовые styles предсказуемее, но философия разная. -На интервью важно услышать trade-off: reset дает больше контроля, но требует явно восстановить полезные defaults, а -normalize-подход меньше ломает привычное поведение браузера. Выбор должен быть частью design system или base styles, а -не случайным package в dependencies. +**Reset CSS** обычно удаляет большую часть UA styling: + +```css +h1, +h2, +p { + margin: 0; +} +``` + +Плюс — design system получает почти чистый canvas. Минус — полезные defaults нужно восстановить: typography, list +markers, focus indicators, form control behavior и spacing. + +**Normalize CSS** старается сохранить нормальные browser conventions, но исправить известные cross-browser differences. +То есть цель не «все обнулить», а «сделать разумные defaults более согласованными». + +В современном application/design system часто достаточно небольшого **base layer**: + +```css +@layer base { + *, + *::before, + *::after { + box-sizing: border-box; + } + + body { + margin: 0; + } + + button, + input, + textarea, + select { + font: inherit; + } +} +``` + +После этого component library явно определяет остальные contracts. + +Опасный reset: + +```css +* { + all: unset; +} +``` + +может убрать display, inherited behavior и accessibility-related visual defaults. Особенно опасно удалять focus outline +без равноценной замены. + +Выбор reset/normalize зависит от продукта: content site часто выигрывает от browser typography defaults, а строгая +cross-platform design system может хотеть больше контроля. + +На интервью: **reset покупает контроль ценой восстановления defaults; normalize покупает совместимость, сохраняя +platform behavior. Сильный production answer — иметь минимальный осознанный base layer**. @@ -328,15 +1007,65 @@ normalize-подход меньше ломает привычное поведе **Короткий ответ** -Частые ошибки: огромный unused CSS, слишком широкие global rules, тяжелые shadows/filters на больших списках, -transition: all, layout properties в частых анимациях и глубокая зависимость от DOM-структуры. Эффективность проверяют -DevTools, Coverage и реальными сценариями, а не только визуальным результатом. +Проблемы бывают на разных стадиях: oversized/unused CSS, частый style recalculation, layout thrashing, дорогой paint, +лишние layers и тяжелые animations. Оптимизировать нужно по Performance/Coverage data, а не по мифам о selectors. **Полный ответ** -Частые ошибки: огромный unused CSS, слишком широкие global rules, тяжелые shadows/filters на больших списках, -`transition: all`, layout properties в частых анимациях и глубокая зависимость от DOM-структуры. Эффективность проверяют -DevTools, Coverage и реальными сценариями, а не только визуальным результатом. +«Медленный CSS» полезно разделять по стадиям rendering pipeline. + +**1. Delivery/parsing** + +Большой global stylesheet с десятками килобайт unused rules увеличивает network, parse и style data. Code splitting, +critical CSS strategy и удаление legacy rules часто дают больше, чем micro-optimization selector syntax. + +**2. Style recalculation** + +Частые DOM/class mutations на большом tree могут постоянно инвалидировать styles. Особенно стоит проверить components, +которые на scroll/mousemove меняют classes у большого числа descendants. + +**3. Layout** + +Анимация geometry properties: + +```css +.panel { + transition: width 300ms; +} +``` + +может запускать layout каждый frame и затрагивать соседние elements. Для purely visual movement часто лучше `transform`, +если semantics/layout действительно не должны меняться. + +**4. Paint** + +Большие blurred shadows, filters, masks, gradients и massive areas с frequent repaint могут быть дорогими даже без +layout. + +**5. Compositing** + +`transform`/`opacity` часто compositor-friendly, но это не означает «бесплатно». Принудительное создание множества +layers через бессистемный `will-change` расходует memory и может ухудшить performance. + +Типичные anti-patterns: + +```css +* { + transition: all 300ms; +} +``` + +`transition: all` трудно контролировать: будущая declaration неожиданно начинает анимироваться. Лучше перечислять +конкретные properties. + +Также performance и maintainability часто деградируют вместе из-за огромных global selectors, duplicated rules и styles, +жестко связанных с глубокой DOM structure. + +Проверять нужно реальный scenario в DevTools: Coverage для unused CSS, Performance trace для Recalculate Style/Layout/ +Paint, FPS/long frames для animations и field metrics для user-visible результата. + +На интервью: **не существует одной категории «CSS performance». Нужно определить stage — bytes, style, layout, paint или +composite — и оптимизировать измеренный bottleneck**.