Связывайте ответ с браузером, доступностью, производительностью и пользовательским состоянием — именно там видна зрелость frontend-инженера.
Вопросы и ответы
12 подробных ответов
01Что такое семантическая вёрстка и зачем она нужна (доступность, SEO, поддержка)?
junior
Короткий ответ: Семантическая вёрстка — это использование HTML-тегов по их смыслу (<nav>, <article>, <button>), а не <div> на всё подряд. Браузер и вспомогательные технологии понимают структуру документа, что даёт доступность, SEO и читаемый код.
Подробно:
- Доступность (a11y) — скринридеры строят дерево доступности по тегам:
<nav>,<main>,<h1>дают навигацию по ориентирам и заголовкам.<div>-суп озвучивается как бесструктурный текст. - SEO — поисковики выделяют
<article>,<h1>–<h6>,<time>как сигналы структуры и важности контента. - Поддерживаемость —
<header>/<footer>/<aside>самодокументируют разметку; не нужно читать классы, чтобы понять роль блока. - Бесплатное поведение —
<button>фокусируется и реагирует на Enter/Space,<a>навигируется,<details>сворачивается — без JS.
<!-- ❌ div-суп -->
<div class="nav"><div class="link" onclick="go()">Главная</div></div>
<!-- ✅ семантика -->
<header>
<nav aria-label="Основная">
<a href="/">Главная</a>
</nav>
</header>
<main>
<article>
<h1>Заголовок статьи</h1>
<time datetime="2026-06-30">30 июня 2026</time>
</article>
</main>
⚠️ Частая ошибка: делать кликабельный <div onclick> вместо <button> — теряются фокус, клавиатура и роль для скринридера.
02Объясните блочную модель CSS и как `box-sizing: border-box` меняет расчёт размеров.
junior
Короткий ответ: Каждый элемент — это вложенные слои: content → padding → border → margin. По умолчанию (content-box) width задаёт только контент, а padding и border прибавляются сверху. border-box включает padding и border внутрь заданной width.
Подробно:
- content — область контента, на которую и ссылается
width/heightприcontent-box. - padding — внутренний отступ, окрашивается фоном элемента.
- border — рамка между padding и margin.
- margin — внешний отступ, прозрачный; у соседних блоков по вертикали схлопывается (margin collapse).
┌──────────── margin ────────────┐
│ ┌───────── border ─────────┐ │
│ │ ┌────── padding ─────┐ │ │
│ │ │ content │ │ │
│ │ └────────────────────┘ │ │
│ └──────────────────────────┘ │
└────────────────────────────────┘
/* content-box: реальная ширина = 200 + 2*16 + 2*2 = 236px */
.a { box-sizing: content-box; width: 200px; padding: 16px; border: 2px solid; }
/* border-box: реальная ширина ровно 200px, padding/border внутри */
.b { box-sizing: border-box; width: 200px; padding: 16px; border: 2px solid; }
/* типичный сброс */
*, *::before, *::after { box-sizing: border-box; }
⚠️ Частая ошибка: задать width: 100% плюс padding на content-box — блок вылезает за контейнер; border-box это чинит.
03Flexbox или Grid: чем отличаются и когда выбирать какой?
middle
Короткий ответ: Flexbox — одномерная раскладка (строка ИЛИ колонка), Grid — двумерная (строки И колонки одновременно). Flex хорош для распределения элементов вдоль одной оси, Grid — для управления макетом по сетке.
Подробно:
- Flexbox — раскладывает контент вдоль главной оси; размеры часто определяются содержимым (content-out). Идеален для тулбаров, навбаров, кнопок в ряд, центрирования.
- Grid — задаёшь сетку шаблоном (layout-in), помещаешь элементы в ячейки/области. Идеален для общего макета страницы, карточных сеток, выравнивания по строкам И колонкам.
- Совмещают — типично Grid для каркаса страницы, Flex внутри компонентов.
| Критерий | Flexbox | Grid |
|---|---|---|
| Оси | одна (1D) | две (2D) |
| Подход | от контента | от шаблона сетки |
| Сильная сторона | распределение в ряд/колонку | строки + колонки сразу |
| Пример | навбар, тулбар | макет страницы, галерея |
/* Flex: элементы в ряд, прижать края, центр по вертикали */
.toolbar { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
/* Grid: адаптивная сетка карточек без медиазапросов */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
⚠️ Частая ошибка: строить двумерный макет на вложенных flex-контейнерах — колонки не выравниваются между строками; это работа для Grid.
04Как считается специфичность селекторов и как каскад разрешает конфликты? Где тут `!important` и `@layer`?
middle
Короткий ответ: Специфичность — это тройка (id, class, type): # весит больше любого числа классов, класс — больше любого числа тегов. При равной специфичности побеждает то, что объявлено позже. !important и @layer стоят над обычным каскадом.
Подробно:
- Подсчёт (a,b,c) — a = id-селекторы, b = классы/атрибуты/псевдоклассы, c = теги/псевдоэлементы. Сравнение слева направо.
- Inline-стиль — весит выше любого селектора (условно «1,0,0,0»).
- При равенстве — порядок — выигрывает последнее правило в источнике.
!important— поднимает объявление над нормальным каскадом; перебивается только другим!importantс большей специфичностью.@layer— каскадные слои: порядок слоёв важнее специфичности внутри них (поздний слой бьёт ранний), что позволяет управлять приоритетом без гонки специфичности.
/* специфичность: */
a { } /* 0,0,1 */
.btn { } /* 0,1,0 — бьёт a */
nav a.btn { } /* 0,1,2 */
#cta { } /* 1,0,0 — бьёт всё выше */
@layer base, theme; /* theme объявлен позже → побеждает */
@layer base { .btn { color: black; } }
@layer theme { .btn { color: rebeccapurple; } } /* выиграет */
⚠️ Частая ошибка: гасить конфликты через !important — это эскалация войны специфичности; чаще правильнее каскадные слои или менее специфичный селектор.
05Чем отличаются значения `position`: static, relative, absolute, fixed, sticky? Приведите реальный пример для sticky.
middle
Короткий ответ: static — обычный поток; relative — смещение от своего места без выпадения из потока; absolute — позиционирование относительно ближайшего позиционированного предка с выпадением из потока; fixed — относительно вьюпорта; sticky — гибрид: ведёт себя как relative, пока не достигнет порога прокрутки, затем «прилипает».
Подробно:
- static — значение по умолчанию;
top/left/z-indexигнорируются. - relative — сдвигается смещениями, но место в потоке сохраняет; создаёт контекст для
absolute-потомков. - absolute — выпадает из потока, отсчитывается от ближайшего предка с
position != static. - fixed — выпадает из потока, привязан к вьюпорту (баннеры, модалки).
- sticky — нужен порог (
top,bottom…) и прокручиваемый контейнер; прилипает в пределах своего родителя.
| position | В потоке | Точка отсчёта |
|---|---|---|
| static | да | — |
| relative | да | своё место |
| absolute | нет | позиционированный предок |
| fixed | нет | вьюпорт |
| sticky | да→прилип | предок + порог скролла |
/* заголовок таблицы, прилипающий к верху при прокрутке */
thead th { position: sticky; top: 0; background: white; z-index: 1; }
⚠️ Частая ошибка: sticky не работает, если у родителя overflow: hidden/auto или не задан порог (top).
06Что такое контекст наложения (stacking context) и почему больший `z-index` иногда ничего не меняет?
senior
Короткий ответ: z-index сравнивается только внутри одного контекста наложения. Если два элемента в разных контекстах, порядок задают их предки-контексты, а не их собственные z-index — поэтому «999999» бывает бессилен против соседнего поддерева.
Подробно:
- Корень —
<html>создаёт корневой контекст наложения. - Что создаёт новый контекст —
position != static+z-index≠ auto; любойopacity < 1;transform,filter,perspective,will-change;isolation: isolate; flex/grid-потомок сz-index. - Сравнение локально — внутри контекста элементы сортируются по
z-index, затем по порядку в DOM. - Между контекстами — решает
z-indexих родительских контекстов, а не вложенных детей.
root (html)
├─ A z-index:1 ← создаёт контекст A
│ └─ A1 z-index:9999 (заперт внутри A)
└─ B z-index:2 ← контекст B
└─ B1 z-index:1
B1 рисуется ПОВЕРХ A1: B(2) > A(1), хотя 9999 > 1
/* «починка»: повысить родительский контекст, а не ребёнка */
.panel-a { position: relative; z-index: 3; }
/* или изолировать поддерево от соседей */
.widget { isolation: isolate; }
⚠️ Частая ошибка: крутить z-index ребёнку, забыв, что opacity/transform на предке уже заперли его в отдельном контексте.
07Как делать адаптивную вёрстку: mobile-first, относительные единицы, медиазапросы и современные контейнерные запросы?
middle
Короткий ответ: Mobile-first — пишем базовые стили под узкий экран, расширяем медиазапросами min-width. Используем относительные единицы (rem, em, %, vw) вместо пикселей. Контейнерные запросы реагируют на размер родителя, а не вьюпорта — это адаптивные компоненты.
Подробно:
- Mobile-first — база без медиазапросов, затем
@media (min-width: …)наращивает макет; меньше переопределений. - Относительные единицы —
remот корня (масштабируется со шрифтом пользователя),emот родителя,%/vwот контекста; пиксели — для границ/теней. - Медиазапросы — реагируют на вьюпорт; брейкпоинты лучше задавать по контенту, а не по моделям устройств.
- Контейнерные запросы — компонент адаптируется к своему контейнеру: один и тот же блок по-разному выглядит в сайдбаре и в основной колонке.
/* mobile-first медиазапрос */
.grid { display: grid; gap: 16px; }
@media (min-width: 48rem) { .grid { grid-template-columns: 1fr 1fr; } }
/* контейнерные запросы */
.card-wrap { container-type: inline-size; }
@container (min-width: 30rem) {
.card { grid-template-columns: 120px 1fr; }
}
⚠️ Частая ошибка: верстать desktop-first с max-width и кучей переопределений — мобильный CSS раздувается; и задавать размеры шрифта в px, ломая пользовательский зум.
08Как масштабировать CSS в большом приложении: BEM, CSS Modules, CSS-in-JS, utility-first (Tailwind) — компромиссы?
senior
Короткий ответ: Все четыре решают одну проблему — глобальную природу CSS и конфликты имён. BEM — соглашение об именах, CSS Modules — локальные классы на этапе сборки, CSS-in-JS — стили в JS с динамикой, utility-first — мелкие готовые классы в разметке.
Подробно:
- BEM —
block__element--modifier; работает с любым стеком, ноль инструментов, но имена длинные и дисциплина на людях. - CSS Modules —
styles.buttonхешируется в уникальный класс; изоляция по умолчанию, обычный CSS, но без рантайм-динамики. - CSS-in-JS — стили колокированы с компонентом, легко завязать на пропсы/тему; цена — рантайм или сложный билд; тренд сместился к zero-runtime решениям.
- Utility-first (Tailwind) — композиция из атомарных классов; быстрая разработка, маленький итоговый CSS, но «шумная» разметка и кривая входа.
| Подход | Изоляция | Динамика | Цена |
|---|---|---|---|
| BEM | по соглашению | нет | дисциплина |
| CSS Modules | сборка | слабая | нужен бандлер |
| CSS-in-JS | рантайм/сборка | сильная | вес/рантайм |
| Utility (Tailwind) | глобально-атомарно | через классы | шум в HTML |
⚠️ Частая ошибка: смешивать сразу несколько методологий без правил — растёт и итоговый CSS, и когнитивная нагрузка; выберите одну основную.
09Чем псевдоклассы отличаются от псевдоэлементов? Как работают `:is()`, `:where()`, `:has()` и их специфичность?
middle
Короткий ответ: Псевдокласс (:hover, :focus) описывает состояние существующего элемента — один двоеточие; псевдоэлемент (::before, ::first-line) создаёт/стилизует часть, которой нет в DOM — два двоеточия. :is()/:where() группируют селекторы, :has() — это «родительский» селектор.
Подробно:
- Псевдокласс — состояние/положение:
:hover,:nth-child(2),:checked. Синтаксис:name. - Псевдоэлемент — виртуальная часть:
::before,::after,::placeholder,::marker. Синтаксис::name. :is(...)— сокращает группы; берёт специфичность самого «тяжёлого» аргумента.:where(...)— то же, но специфичность всегда0,0,0— удобно для базовых/сбрасываемых стилей.:has(...)— матчит элемент по его потомкам/состоянию детей (родительский селектор); специфичность — по самому тяжёлому аргументу.
/* без :is — длинно */
.post h1, .post h2, .post h3 { margin-top: 0; }
/* с :is — компактно (специфичность 0,1,1) */
.post :is(h1, h2, h3) { margin-top: 0; }
/* :where — нулевая специфичность, легко переопределить */
:where(ul, ol) { padding-left: 1rem; }
/* :has — стилизуем форму, если внутри есть невалидное поле */
form:has(input:invalid) .submit { opacity: .5; }
⚠️ Частая ошибка: считать, что :is() имеет нулевую специфичность — её даёт только :where().
10Как работают CSS-переменные (custom properties): каскад/scope, рантайм-тематизация и чем отличаются от Sass-переменных?
middle
Короткий ответ: Custom properties (--name) наследуются и каскадируются как обычные свойства, живут в рантайме и читаются через var(). В отличие от Sass-переменных, которые компилятор подставляет на этапе сборки, CSS-переменные можно менять динамически — через классы, медиазапросы, JS.
Подробно:
- Объявление и чтение —
--gap: 16px;затемgap: var(--gap, 8px);(второй аргумент — фолбэк). - Каскад и scope — значение наследуется вниз по дереву; переопределяется на любом селекторе/
:root/инлайн. - Рантайм — реагируют на
:hover, медиазапросы, тему; читаются/пишутся из JS черезgetPropertyValue/setProperty. - Против Sass — Sass-переменные статичны: после компиляции их нет в CSS, тему на лету не переключить.
:root { --bg: white; --fg: #111; }
[data-theme="dark"] { --bg: #111; --fg: #eee; } /* смена темы переопределением */
.card { background: var(--bg); color: var(--fg); }
@media (prefers-color-scheme: dark) {
:root { --bg: #111; --fg: #eee; }
}
// чтение/запись из JS
const root = document.documentElement;
root.style.setProperty('--bg', '#0b0b0b');
⚠️ Частая ошибка: ждать от var() арифметики как у Sass — для вычислений нужен calc(), а медиазапросы по самим переменным не работают.
11В чём разница между block, inline и inline-block, и что такое нормальный поток документа?
junior
Короткий ответ: Block-элементы занимают всю ширину строки и принимают любые размеры/отступы; inline идут в строку по содержимому и игнорируют width/вертикальные margin; inline-block — в строке, но с управляемыми размерами. Нормальный поток — порядок, в котором элементы раскладываются сверху вниз (block) и слева направо (inline) без позиционирования.
Подробно:
- block (
<div>,<p>,<section>) — новая строка, ширина по умолчанию 100%, уважаетwidth/height, все margin/padding. - inline (
<span>,<a>,<strong>) — в потоке текста;width/heightи вертикальные margin игнорируются, горизонтальные — работают. - inline-block — в строке, как inline, но с полноценными размерами и отступами — классика для «кнопок» в ряд.
- Нормальный поток — раскладка по умолчанию;
position,float, flex/grid выводят из него.
| display | Перенос строки | width/height | Верт. margin |
|---|---|---|---|
| block | да | да | да |
| inline | нет | нет | нет |
| inline-block | нет | да | да |
.tag { display: inline-block; width: 80px; padding: 4px 8px; }
⚠️ Частая ошибка: задавать width/height на чистом inline-элементе и ждать эффекта — их там не существует; нужен inline-block или block.
12Что вызывает layout thrashing / рефлоу при работе CSS и JS, и как этого избежать?
senior
Короткий ответ: Layout thrashing — это многократное чередование чтения геометрии (offsetWidth, getBoundingClientRect) и записи в DOM в одном кадре. Каждое чтение после записи заставляет браузер синхронно пересчитывать layout (forced reflow). Лечится батчингом: сначала все чтения, потом все записи.
Подробно:
- Reflow (layout) — пересчёт геометрии; запускается изменением размеров/позиций и чтением layout-свойств.
- Read→write→read в цикле — каждое чтение инвалидирует кэш и форсит синхронный reflow → джанк.
- Батчинг — сгруппировать все чтения, затем все записи; читать вне циклов.
transform/opacity— анимировать их вместоtop/left/width: они идут на композитор, минуя layout и paint.will-change/contain— подсказать браузеру слой заранее и ограничить область пересчёта (contain: layout).
// ❌ thrashing: чтение и запись чередуются
for (const el of items) {
el.style.width = el.offsetWidth + 10 + 'px'; // read→write каждую итерацию
}
// ✅ батчинг: сначала читаем, потом пишем
const widths = items.map(el => el.offsetWidth); // все чтения
items.forEach((el, i) => { el.style.width = widths[i] + 10 + 'px'; }); // все записи
⚠️ Частая ошибка: анимировать width/top в цикле и читать getBoundingClientRect тут же — браузер форсит reflow на каждом кадре; используйте transform и батчинг.
Источники
Источники и редакционная политика
Материалы RecallDeck сопоставлены с официальной документацией и открытыми публикациями компаний, когда первичный источник доступен. Мы не связаны с упомянутыми работодателями, не публикуем конфиденциальные задания и не продаём места в подборках. Формат найма может меняться — уточняйте его у рекрутера.
От чтения к воспроизведению
Отрепетируйте полный цикл интервью.
RecallDeck возвращает сложные темы по расписанию и помогает удерживать в памяти язык, SQL, архитектуру и поведенческие истории.