Лабораторная работа №3. Стилизация интерфейса Web-приложения
1. Теория
1.0. Цель работы
Сформировать у обучающегося системное понимание CSS как слоя представления и компоновки интерфейса Web-приложения, который:
- управляет визуальной и пространственной организацией UI (layout, типографика, цвета, состояния);
- работает по правилам каскада, наследования и специфичности;
- проектирует адаптивный интерфейс (mobile-first, breakpoints, responsive-паттерны);
- обеспечивает масштабируемую архитектуру стилей (компоненты, методологии, организация файлов);
- интегрируется с HTML (семантика, классы/атрибуты) и JavaScript (классы, состояния, анимации).
Закрепить навык архитектурного описания UI через схему:
UI (HTML) → DOM → CSS (cascade/layout) → JS (events/state) → API (HTTP) → DB
Подводка (почему это важно): В реальном проекте CSS — это не «раскраска». Это правила отображения данных и состояний. Один и тот же UI-компонент должен:
- выглядеть одинаково во всех местах проекта,
- вести себя предсказуемо при изменениях (добавили блок → не “поехало”),
- корректно адаптироваться под разные экраны,
- быть управляемым из JS через состояния (
is-open,is-loading,is-error), - оставаться поддерживаемым командой (архитектура, методология, ограничения).
1.1. Введение в CSS
1.1.1. Назначение CSS: разделение структуры и представления
HTML описывает структуру и смысл (семантику): что является заголовком, списком, кнопкой, формой. CSS описывает как это выглядит и как расположено: сетка, отступы, цвет, типографика, состояние.
Подводка: если вы смешиваете HTML и CSS (например, делаете внешний вид через inline-стили), вы теряете масштабируемость: невозможно быстро поменять тему, дизайн-систему, адаптивность.
Пример “плохо” (inline-стили ломают поддержку):
<button style="background: red; color: white; padding: 12px 16px;">
Купить
</button>
Пример “нормально” (контракт через класс):
<button class="btn btn--primary">Купить</button>
.btn { padding: 12px 16px; border-radius: 10px; }
.btn--primary { background: #e11d48; color: #fff; }
1.1.2. Принцип каскадности
Каскад — механизм, по которому браузер выбирает итоговые значения свойств, когда на элемент влияют несколько правил.
Каскад учитывает (в упрощении):
- важность (
!important), - происхождение (user-agent / user / author),
- специфичность селектора,
- порядок правил (ниже в файле → при равенстве выигрывает).
Подводка: каскад — это “разрешитель конфликтов”. Если вы не понимаете каскад, вы начинаете “лечить” стили !important, а это разрушает архитектуру.
1.1.3. Принцип наследования
Наследование — передача некоторых свойств от родителя к потомкам. Наследуются в основном текстовые свойства (color, font-family, line-height), но не layout-свойства (например, margin не наследуется).
Пример:
.page {
font-family: system-ui, sans-serif;
color: #111827;
line-height: 1.5;
}
<div class="page">
<h1>Заголовок</h1>
<p>Текст наследует цвет и шрифт.</p>
</div>
Подводка: наследование — главный инструмент “глобальной консистентности” (типографика, цвет текста), но его нужно держать под контролем (не делать “глобальные” правила, которые ломают компоненты).
1.2. Способы подключения CSS
1.2.1. Inline (в атрибуте style)
<p style="color: #e11d48; font-weight: 700;">Акцентный текст</p>
Когда допустимо: быстрый прототип, единичная правка, письма (email), генерация стилей из CMS. Минусы: не переиспользуется, сложно сопровождать, конфликтует с архитектурой.
1.2.2. Internal (внутри <style>)
<head>
<style>
.card { padding: 16px; border: 1px solid #e5e7eb; }
</style>
</head>
Подводка: в учебных мини-работах допустимо, но в проекте это усложняет кеширование и поддержку.
1.2.3. External (отдельный файл)
<link rel="stylesheet" href="./styles.css" />
Это основной промышленный способ. Плюсы: кеширование, структура проекта, переиспользование, командная работа.
1.2.4. @import
@import url("./base.css");
@import url("./components/card.css");
Подводка: @import может ухудшать загрузку (особенно при каскаде импортов). В современных проектах чаще используют сборщики/бандлеры, а в “чистом CSS” — аккуратно и минимально.
1.3. Селекторы CSS
1.3.1. Базовые селекторы
По тегу:
button { cursor: pointer; }
По классу:
.btn { padding: 12px 16px; }
По id:
#app { min-height: 100vh; }
Подводка:
idдолжен быть уникальным, и стили через#idчасто создают проблемы со специфичностью.- В UI-архитектуре чаще используют классы и data-атрибуты.
1.3.2. Комбинаторы
Потомок (любой уровень вложенности):
.card p { margin: 0; }
Прямой потомок:
.nav > a { text-decoration: none; }
Соседний элемент (+):
label + input { margin-top: 6px; }
Общий сосед (~):
input:focus ~ .hint { opacity: 1; }
Подводка: чем сложнее селектор, тем выше риск “ломкости”: небольшая правка HTML ломает стили. Поэтому архитектурно лучше иметь классы компонентов.
1.3.3. Атрибутные селекторы
input[type="email"] { border-color: #93c5fd; }
button[data-action="add-to-cart"] { font-weight: 700; }
Подводка: data-* удобно для “контракта” с JS: CSS может стилизовать, а JS — находить/управлять.
1.3.4. Псевдоклассы
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.field:focus { outline: 2px solid #60a5fa; }
.item:nth-child(2n) { background: #f9fafb; }
Подводка: псевдоклассы — это модель состояний без JS: hover/focus/active — критично для UX и доступности.
1.3.5. Псевдоэлементы
.badge::before { content: "● "; }
.card::after { content: ""; display: block; height: 1px; background: #e5e7eb; }
1.3.6. Таблица специфичности (приоритет селекторов)
| Селектор | Пример | Специфичность (A,B,C) |
|---|---|---|
| Inline style | style="..." |
выше всех (условно 1,0,0,0) |
#id |
#header |
(1,0,0) |
.class, [attr], :pseudo-class |
.btn, [type="text"], :hover |
(0,1,0) |
tag, ::pseudo-element |
button, ::before |
(0,0,1) |
*, комбинаторы |
*, >, +, ~ |
(0,0,0) |
Подводка: если вы начали “побеждать” стили повышением специфичности, вы строите систему конфликтов. В архитектуре лучше управлять правилами через компоненты и слои.
1.4. Каскадность и специфичность
1.4.1. Что такое каскад (как браузер выбирает итоговый стиль)
Когда несколько правил задают одно свойство, браузер выбирает итог по схеме:
!important(если есть),- более специфичный селектор,
- правило, объявленное позже.
1.4.2. !important как крайняя мера
.btn { color: black !important; }
Подводка: !important должен быть исключением (например, утилитарные классы, временный фикс, переопределение сторонних библиотек). Иначе каскад превращается в “гонку вооружений”.
1.4.3. Порядок подключения файлов
<link rel="stylesheet" href="./base.css" />
<link rel="stylesheet" href="./components.css" />
<link rel="stylesheet" href="./pages/catalog.css" />
Подводка: порядок — это часть архитектуры: базовые правила → компоненты → страницы → overrides.
1.4.4. Примеры конфликтов и решения
Конфликт:
button { background: #111827; color: white; }
.btn { background: #e11d48; }
<button class="btn">Купить</button>
Итог: background будет #e11d48, потому что .btn специфичнее, а color останется белым из button.
Плохое “решение” через специфичность:
body main .toolbar .btn { background: #e11d48; }
Хорошее решение (слой компонента + модификатор):
.btn { background: #111827; color: #fff; }
.btn--primary { background: #e11d48; }
1.5. Цвета и фоны
1.5.1. Базовые свойства
.text-muted { color: #6b7280; }
.card { background-color: #ffffff; }
.hero { background-image: url("./img/bg.jpg"); background-size: cover; }
1.5.2. Градиенты и прозрачность
.hero {
background-image: linear-gradient(135deg, rgba(225,29,72,0.9), rgba(59,130,246,0.6));
}
1.5.3. CSS-переменные (custom properties)
:root {
--color-bg: #0b1220;
--color-text: #e5e7eb;
--radius: 16px;
}
.page {
background: var(--color-bg);
color: var(--color-text);
border-radius: var(--radius);
}
Подводка: переменные — фундамент темизации и дизайн-системы: один источник правды вместо “магических” цветов по проекту.
1.6. Работа с текстом и типографикой
body {
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
font-size: 16px;
line-height: 1.5;
}
h1 { font-size: 32px; font-weight: 800; }
p { letter-spacing: 0.2px; text-align: left; }
.upper { text-transform: uppercase; }
1.6.1. Web-шрифты (через @font-face)
@font-face {
font-family: "MyFont";
src: url("./fonts/MyFont.woff2") format("woff2");
font-display: swap;
}
.title { font-family: "MyFont", system-ui, sans-serif; }
Подводка: font-display: swap снижает риск “невидимого текста” при загрузке шрифта.
1.7. Box Model (фундамент)
1.7.1. Состав Box Model
- content — содержимое (текст/контент)
- padding — внутренний отступ
- border — граница
- margin — внешний отступ
1.7.2. Схема Box Model
┌─────────────────────────────── margin ───────────────────────────────┐
│ ┌──────────────────────────── border ─────────────────────────────┐ │
│ │ ┌──────────────────────── padding ───────────────────────────┐ │ │
│ │ │ content │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────┘
1.7.3. box-sizing, overflow
/* промышленный дефолт */
*, *::before, *::after { box-sizing: border-box; }
.card {
width: 320px;
padding: 16px;
border: 1px solid #e5e7eb;
overflow: hidden;
}
Подводка: без border-box расчёты размеров становятся непредсказуемыми, особенно в сетках и адаптиве.
1.8. Позиционирование
1.8.1. Виды позиционирования
.static-example { position: static; }
.relative-example { position: relative; top: 6px; }
.absolute-example { position: absolute; top: 0; right: 0; }
.fixed-example { position: fixed; bottom: 16px; right: 16px; }
.sticky-example { position: sticky; top: 0; }
1.8.2. Практические кейсы
Бейдж на карточке (absolute внутри relative):
.card { position: relative; }
.card__badge {
position: absolute;
top: 12px;
right: 12px;
padding: 6px 10px;
border-radius: 999px;
background: #111827;
color: #fff;
}
Подводка: absolute работает относительно ближайшего предка с position: relative; — это типовая основа UI-бейджей, меню, модалок.
1.9. Flexbox
1.9.1. Базовые свойства
.toolbar {
display: flex;
gap: 12px;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
1.9.2. Пример макета интерфейса на Flexbox (Header + Controls)
HTML:
<header class="toolbar">
<h1 class="toolbar__title">Каталог</h1>
<div class="toolbar__actions">
<button class="btn">Фильтры</button>
<button class="btn btn--primary">Добавить</button>
</div>
</header>
CSS:
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.toolbar__actions {
display: flex;
gap: 10px;
align-items: center;
}
Подводка: Flexbox идеален для “линейных” компоновок: панели, строки, группы кнопок, выравнивание по оси.
1.10. CSS Grid
1.10.1. Базовые свойства
.grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(3, 1fr);
}
1.10.2. Auto-fit / auto-fill
.catalog {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
1.10.3. Grid areas
.page {
display: grid;
grid-template-columns: 280px 1fr;
grid-template-areas: "sidebar main";
gap: 16px;
}
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
1.10.4. Отличие Flexbox и Grid
| Критерий | Flexbox | Grid |
|---|---|---|
| Модель | 1-мерная (ряд/колонка) | 2-мерная (ряд + колонка) |
| Лучше для | панели, группы, выравнивание | сетки карточек, layout страниц |
| Контроль | распределение по оси | точное размещение в сетке |
Подводка: не “Flex или Grid”, а “Flex внутри Grid”: страница строится Grid, а внутри компонентов используется Flex.
1.11. Адаптивный дизайн
1.11.1. Media queries и breakpoints
.card { padding: 16px; }
@media (max-width: 640px) {
.card { padding: 12px; }
}
1.11.2. Mobile-first подход
/* мобильный по умолчанию */
.layout { display: grid; gap: 12px; }
/* расширяем для больших экранов */
@media (min-width: 900px) {
.layout { grid-template-columns: 280px 1fr; }
}
Подводка: mobile-first снижает количество переопределений: вы “наращиваете” интерфейс по мере роста экрана.
1.11.3. Пример адаптивной карточки
HTML:
<article class="product-card">
<img class="product-card__img" src="./product.jpg" alt="Товар" />
<div class="product-card__body">
<h3 class="product-card__title">Сыворотка</h3>
<p class="product-card__price">1999 ₽</p>
<button class="btn btn--primary">В корзину</button>
</div>
</article>
CSS:
.product-card {
display: grid;
grid-template-columns: 96px 1fr;
gap: 12px;
padding: 16px;
border: 1px solid #e5e7eb;
border-radius: 16px;
}
.product-card__img {
width: 96px;
height: 96px;
object-fit: cover;
border-radius: 12px;
}
@media (max-width: 520px) {
.product-card {
grid-template-columns: 1fr;
}
.product-card__img {
width: 100%;
height: 180px;
}
}
1.12. Анимации и переходы
1.12.1. transition + transform
.btn {
transition: transform 160ms ease, box-shadow 160ms ease;
}
.btn:hover {
transform: translateY(-2px);
}
1.12.2. @keyframes + animation
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.03); }
100% { transform: scale(1); }
}
.badge--pulse {
animation: pulse 1.2s ease-in-out infinite;
}
1.12.3. Пример hover-анимации кнопки
.btn--primary {
background: #e11d48;
color: #fff;
border-radius: 12px;
padding: 12px 16px;
transition: transform 160ms ease, filter 160ms ease;
}
.btn--primary:hover { transform: translateY(-2px); filter: brightness(1.05); }
.btn--primary:active { transform: translateY(0); }
Подводка: анимация должна быть “смысловой”: показать интерактивность и обратную связь, а не отвлекать.
1.13. Продвинутые возможности CSS
1.13.1. Переменные, calc(), clamp()
:root { --space: 16px; }
.container { padding: calc(var(--space) * 2); }
.title {
font-size: clamp(20px, 2.5vw, 36px);
}
1.13.2. filter, backdrop-filter, object-fit
.avatar { filter: grayscale(30%); }
.glass {
background: rgba(255,255,255,0.08);
backdrop-filter: blur(10px);
}
.media { object-fit: cover; width: 100%; height: 240px; }
1.14. Архитектура CSS (как строится масштабируемый проект)
1.14.1. БЭМ (BEM)
Принцип: блок → элемент → модификатор.
<article class="card card--featured">
<h3 class="card__title">Заголовок</h3>
<p class="card__desc">Описание</p>
</article>
.card { padding: 16px; border-radius: 16px; }
.card__title { font-weight: 800; }
.card--featured { border: 2px solid #e11d48; }
Подводка: БЭМ уменьшает конфликты: стиль компонента не “протекает” на соседние элементы.
1.14.2. SMACSS / Atomic CSS / Модульный подход (идея)
| Подход | Идея | Когда полезен |
|---|---|---|
| SMACSS | деление стилей на категории (Base/Layout/Module/State/Theme) | большие проекты |
| Atomic/Utility | много маленьких классов-утилит | быстрые UI, дизайн-система |
| Модульный | компоненты как модули (card.css, button.css) | командная разработка |
1.14.3. Организация файлов проекта (пример)
styles/
base/
reset.css
tokens.css
typography.css
components/
button.css
card.css
form.css
pages/
catalog.css
main.css
main.css подключает базу и компоненты (или собирается сборщиком).
1.15. Доступность и UX в CSS
1.15.1. Контраст и читабельность
Подводка: визуально красивый интерфейс может быть нечитаемым. Минимум — достаточный контраст текста и фона.
1.15.2. :focus-visible и клавиатура
.btn:focus-visible {
outline: 3px solid #60a5fa;
outline-offset: 2px;
}
1.15.3. hover vs touch
@media (hover: hover) {
.btn:hover { transform: translateY(-2px); }
}
Подводка: на touch-устройствах hover может “залипать”, поэтому проверка hover: hover — взрослая практика.
1.16. Производительность CSS
1.16.1. Минимизация и критический CSS (идея)
- минимизировать CSS для production (сборщик/минификатор),
- выносить критические стили первого экрана (по возможности),
- избегать огромных каскадов переопределений.
1.16.2. DevTools-анализ
Подводка: если интерфейс “тормозит”, проблема часто не в JS, а в layout/paint из-за тяжелых эффектов, огромных теней, анимаций размеров вместо transform.
1.17. Интеграция CSS с JavaScript
1.17.1. Манипуляция классами (основа)
HTML:
<button class="btn" id="toggleFilters">Фильтры</button>
<aside class="filters" id="filtersPanel"></aside>
CSS (состояние):
.filters { display: none; }
.filters.is-open { display: block; }
JS:
const btn = document.querySelector("#toggleFilters");
const panel = document.querySelector("#filtersPanel");
btn.addEventListener("click", () => {
panel.classList.toggle("is-open");
});
Подводка: хорошая практика — управлять UI через классы состояния, а не через element.style = .... Так сохраняется архитектура и каскад.
1.17.2. Динамические стили и анимации через JS (когда уместно)
JS уместен, когда стиль зависит от вычислений/данных (например, прогресс-бар):
progressEl.style.setProperty("--progress", `${value}%`);
.progress::after { width: var(--progress); }
1.18. Типичные ошибки новичков (и как их диагностировать)
-
Inline-стили как основной подход → нет переиспользования, хаос, сложный рефакторинг.
-
Конфликты из-за
#idи высокой специфичности → потом приходится “перебивать” селекторы ещё более сложными. -
!importantвезде → каскад перестаёт работать как система. -
Нет адаптивности → “на моём ноутбуке ок” ≠ работающий интерфейс.
-
Непонимание специфичности → “почему стиль не применился?” решают не анализом, а случайными правками.
Мини-чек диагностики (как думать):
- стиль не применился → проверь селектор (попадает ли), затем специфичность, затем порядок подключений;
- всё “поехало” → проверь box model (
box-sizing), размеры, overflow, сетку (Grid/Flex); - на мобилке плохо → mobile-first, breakpoints, размеры (clamp), touch/hover.
1.19. Мини-схемы для отчёта (CSS в системе UI)
1.19.1. Слои формирования интерфейса
HTML (семантика/структура)
↓
DOM (дерево элементов)
↓
CSS (каскад, специфичность, layout)
↓
Render tree → Layout → Paint
↓
UI (видимый интерфейс)
1.19.2. CSS как “контракт” со структурой и состояниями
HTML: class / data-* ←→ CSS: компоненты/состояния ←→ JS: toggle/state
2. Задание
Цель: закрепить каскадность и специфичность, box model, основы layout (Flexbox/Grid), адаптивность, состояния UI и доступность на материале интерфейса Web-приложения.
Смысл лабораторной работы
Переход от “разметка есть” к “интерфейс выглядит и ведёт себя как система”:
- один и тот же компонент должен корректно выглядеть в разных местах;
- конфликты стилей должны решаться архитектурно, а не
!important; - layout должен быть устойчивым (не “поехал” из-за padding/border);
- интерфейс должен быть адаптивным и управляемым через состояния (классами);
- интерактивные элементы должны быть удобны для клавиатуры (focus).
Ключевой результат
Интерфейсная страница, в которой:
- продемонстрирован конфликт каскада и корректное решение через “компонент + модификатор”;
- включён глобальный
box-sizing: border-box, карточки фиксированной ширины не “раздуваются”; - собрана header-панель на Flexbox с переносом элементов;
- каталог карточек построен на Grid (
auto-fit/minmax) и адаптирован под мобильный; - панель фильтров открывается/закрывается через JS (
classList.toggle) и CSS-класс состояния.is-open; - реализован
:focus-visibleи проверена Tab-навигация.
2.1. Задания на закрепление теоретического материала (выполнить до стилизации предметной области)
Данный блок выполняется в отдельной песочнице practice-lr3.css (или внизу styles.css), чтобы студент мог показать понимание каскада, box model и layout до финальной стилизации проекта.
Требования к оформлению:
- к каждому блоку должен быть маленький демонстрационный HTML-фрагмент (можно прямо в
index.htmlвнутри секции “practice”); - запрещено использовать
!important(в этой ЛР это отдельное правило); - обязательно сделать комментарии в CSS: что конфликтует и почему выигрывает.
2.1.1. Каскад и конфликты
Задача A1. Создать 3 правила, конфликтующие по одному свойству (color) для одного и того же элемента.
HTML (пример):
<section class="practice">
<p id="demoText" class="text text--accent">Текст для проверки каскада</p>
</section>
CSS (пример конфликта):
/* Правило 1 — по тегу (низкая специфичность) */
p { color: #111827; }
/* Правило 2 — по классу (выше) */
.text { color: #2563eb; }
/* Правило 3 — по id (ещё выше) */
#demoText { color: #e11d48; }
Что требуется показать в отчёте:
- какие 3 правила задают
color; - почему выиграло конкретное (специфичность);
- что будет, если поменять порядок правил (для одинаковой специфичности).
Задача A2. Доказать влияние порядка при одинаковой специфичности.
CSS (пример):
.text--accent { color: #16a34a; } /* правило выше */
.text--accent { color: #f97316; } /* правило ниже — должно выиграть */
Ожидаемый результат:
- цвет текста —
#f97316, потому что специфичность одинаковая, выигрывает правило ниже.
Задача A3. Убрать конфликт архитектурно: “компонент + модификатор”, без !important.
HTML (пример):
<p class="label label--danger">Статус: Ошибка</p>
<p class="label label--success">Статус: Успех</p>
CSS (архитектурное решение):
.label {
font-weight: 700;
padding: 6px 10px;
border-radius: 999px;
color: #111827; /* базовое */
}
.label--danger { color: #b91c1c; }
.label--success { color: #166534; }
Ожидаемый результат:
- нет “борьбы селекторов”, внешний вид управляется модификаторами.
2.1.2. Box Model и стабильная сетка
Задача B1. Подключить box-sizing: border-box глобально.
CSS (обязательно):
*, *::before, *::after {
box-sizing: border-box;
}
Подводка (что проверяем): при border-box ширина элемента включает padding и border, поэтому карточки в сетке ведут себя предсказуемо.
Задача B2. Сделать карточку товара шириной 320px с padding и border, и проверить, что ширина не растёт.
HTML (пример):
<div class="card-demo">Карточка 320px</div>
CSS (пример):
.card-demo {
width: 320px;
padding: 16px;
border: 2px solid #e5e7eb;
}
Что проверить:
- в DevTools → computed width должен быть 320px, а не 320 + padding + border.
2.1.3. Макет на Flexbox (header-панель)
Задача C1. Собрать header-панель: заголовок слева, кнопки справа.
HTML (пример):
<header class="toolbar">
<h1 class="toolbar__title">Каталог</h1>
<div class="toolbar__actions">
<button class="btn">Фильтры</button>
<button class="btn btn--primary">Добавить</button>
</div>
</header>
CSS (пример):
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.toolbar__actions {
display: flex;
gap: 10px;
align-items: center;
}
Задача C2. Добавить flex-wrap, чтобы на узком экране кнопки переносились.
CSS (добавить):
.toolbar {
flex-wrap: wrap;
}
.toolbar__actions {
flex-wrap: wrap;
}
Ожидаемый результат:
- при уменьшении ширины окна кнопки не ломают layout и уходят на новую строку.
2.1.4. Grid-сетка каталога + адаптив
Задача D1. Сделать сетку карточек auto-fit/minmax.
HTML (пример):
<section class="catalog">
<article class="product-card">Карточка 1</article>
<article class="product-card">Карточка 2</article>
<article class="product-card">Карточка 3</article>
<article class="product-card">Карточка 4</article>
</section>
CSS (пример):
.catalog {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.product-card {
padding: 16px;
border: 1px solid #e5e7eb;
border-radius: 16px;
}
Задача D2. Добавить breakpoint для мобильного: карточка становится “в столбик” (перестройка внутренней структуры карточки).
HTML (пример карточки с медиа + контентом):
<article class="product-card2">
<img class="product-card2__img" src="./product.jpg" alt="Товар" />
<div class="product-card2__body">
<h3 class="product-card2__title">Название</h3>
<p class="product-card2__meta">Цена · Статус</p>
<button class="btn btn--primary">В корзину</button>
</div>
</article>
CSS (пример):
.product-card2 {
display: grid;
grid-template-columns: 96px 1fr;
gap: 12px;
padding: 16px;
border: 1px solid #e5e7eb;
border-radius: 16px;
}
.product-card2__img {
width: 96px;
height: 96px;
object-fit: cover;
border-radius: 12px;
}
@media (max-width: 520px) {
.product-card2 {
grid-template-columns: 1fr; /* “в столбик” */
}
.product-card2__img {
width: 100%;
height: 180px;
}
}
Ожидаемый результат:
- на мобильном изображение сверху, контент ниже, ничего не выходит за границы.
2.1.5. Интеграция с JS (состояния)
Задача E1. Реализовать панель фильтров, которая открывается/закрывается через classList.toggle.
HTML (пример):
<button class="btn" id="btnFilters" type="button">Фильтры</button>
<aside class="filters" id="filtersPanel" aria-hidden="true">
<h2 class="filters__title">Фильтры</h2>
<label class="field">
Статус
<select>
<option>Все</option>
<option>new</option>
<option>done</option>
</select>
</label>
</aside>
CSS (состояние):
.filters {
display: none;
padding: 16px;
border: 1px solid #e5e7eb;
border-radius: 16px;
}
.filters.is-open {
display: block;
}
JS (пример):
const btn = document.querySelector("#btnFilters");
const panel = document.querySelector("#filtersPanel");
btn.addEventListener("click", () => {
panel.classList.toggle("is-open");
const isOpen = panel.classList.contains("is-open");
panel.setAttribute("aria-hidden", String(!isOpen));
});
Ожидаемый результат:
- открытие/закрытие происходит через класс состояния, а не через inline-стили.
2.1.6. Доступность
Задача F1. Добавить :focus-visible для интерактивных элементов.
CSS (пример):
.btn:focus-visible,
a:focus-visible,
select:focus-visible,
input:focus-visible {
outline: 3px solid #60a5fa;
outline-offset: 2px;
}
Задача F2. Проверить Tab-навигацию.
Требования к проверке (описать в отчёте):
- Нажать
Tabи пройти по кнопкам/полям в логичном порядке. - Фокус должен быть видимым на каждом элементе.
- Не должно быть “ловушек фокуса” (когда Tab застревает).
Ожидаемый результат:
- пользователь может управлять интерфейсом без мыши.
2.2. Исходные данные (предметная область)
Для выполнения лабораторной работы используется предметная область, закреплённая за студентом на семестр (как в ЛР-1/ЛР-2).
Требования:
- интерфейс должен содержать: header-панель, панель фильтров, каталог карточек;
- карточки должны быть реального смысла для предметной области (товары/заявки/курсы/записи и т.д.);
- минимум 6–8 карточек для демонстрации Grid-сетки и адаптива.
2.3. Структура проекта (рекомендуется)
Задание: организовать стили и скрипт так, чтобы было понятно, где что лежит.
Минимальный набор файлов:
index.htmlstyles.css(илиstyles/main.css)script.js(только для toggle фильтров)
Подключение:
<link rel="stylesheet" href="./styles.css" />
<script src="./script.js" defer></script>
Ожидаемый результат:
- страница открывается без ошибок, стили применяются, toggle работает.
2.4. Обязательные требования к сдаваемому UI
Студент обязан продемонстрировать:
- Каскад и конфликт: 3 правила на одно свойство + объяснение победителя (специфичность/порядок).
- Архитектурное решение: компонент + модификатор вместо “войны селекторов”, без
!important. - Box model стабильность:
border-boxглобально + карточка 320px не раздувается. - Flex header: заголовок слева, кнопки справа + перенос.
- Grid каталог:
auto-fit/minmax+ адаптив внутренней карточки на мобильном. - Состояния через класс:
.is-openдля панели фильтров, управление через JS toggle. - Доступность:
:focus-visible+ проверка Tab-навигации.
3. Итоговая задача: “Стилизация интерфейса Web-приложения” (ЛР-1 → ЛР-2 → ЛР-3)
Цель: превратить интерфейс из ЛР-1/ЛР-2 в визуально организованный, адаптивный и управляемый по состояниям UI.
3.1. Модификация верстки index.html
Добавить (или оформить) следующие блоки:
header.toolbar(заголовок + кнопки).aside.filters(панель фильтров).section.catalog(сетку карточек).- Демонстрационный блок для каскада (можно скрыть в конце страницы).
3.2. Требования к логике и архитектуре
- стили должны быть читабельны и компонентны (классы, модификаторы);
- запрещено “лечить” конфликты
!important; - все состояния UI реализуются классами (
.is-open, при желании.is-active,.is-loading); - JS делает только переключение состояния (toggle) — дизайн остаётся в CSS.
3.3. Требования к сдаче
В репозитории должны быть:
index.html(структура интерфейса);styles.css(все блокиреализованы);script.js(toggle фильтров);- краткое описание в
README.md: что и где демонстрируется (A–F).
3.3. Для выполнения требуется
- Шаблон отчёта, указанный в описании лабораторной работы.
- Вариант задания, закреплённый за студентом на семестр и опубликованный в ЛМС.
- Требования к оформлению — согласно методическим указаниям в ЛМС или соответствующему разделу документации.
- Ссылка на репозиторий (или архив проекта).
3.4. Запрет
❗ Запрещается использование систем искусственного интеллекта для выполнения лабораторной работы.
Работы, выполненные с использованием ИИ, полностью или частично скопированные, а также не сданные, оцениваются в 0 баллов без возможности доработки.
4. Контрольные вопросы
- Что такое CSS и какую роль он играет в связке HTML → DOM → CSSOM → Render Tree → Layout → Paint?
- Что такое каскад в CSS и какие факторы определяют итоговое значение свойства (важность, специфичность, порядок)?
- Что такое специфичность селекторов и почему селектор
#idобычно “сильнее”, чем.class? - В каких случаях влияет порядок правил в файле, и как это доказать на примере двух одинаковых селекторов?
- Почему использование
!importantсчитается плохой практикой в архитектуре стилей, и чем его заменяют (компонент + модификатор)? - Что такое Box Model и из каких частей он состоит (content/padding/border/margin)?
- Зачем подключают глобально
box-sizing: border-boxи как это влияет на расчёт ширины элемента? - В чём разница между Flexbox и Grid: какая модель 1-мерная, какая 2-мерная, и где что применять?
5. Чек-лист для самопроверки
| Баллы | Критерии оценки |
|---|---|
| 20 | Выполнены все обязательные блоки и они явно демонстрируются на странице (есть HTML для проверки + комментарии в CSS). Реализовано: конфликт 3 правил по одному свойству + доказательство победителя (специфичность и порядок) и устранение конфликта архитектурно (компонент + модификатор) без !important; глобальный box-sizing: border-box + карточка шириной 320px с padding и border, ширина не растёт (проверено в DevTools); header на Flexbox (заголовок слева, кнопки справа) + flex-wrap и корректный перенос на узком экране; каталог на Grid (repeat(auto-fit, minmax(...))) + breakpoint, где карточка перестраивается “в столбик” на мобильном; панель фильтров открывается/закрывается через classList.toggle() и CSS-класс .is-open (без inline-стилей), желательно синхронизирован aria-hidden; добавлен :focus-visible для интерактивных элементов и выполнена проверка Tab-навигации (фокус виден, порядок логичный). Проект оформлен аккуратно: классы компонентные (БЭМ/модификаторы), селекторы не “ломкие”, нет !important. Файлы (index.html, styles.css, script.js) подключены корректно, ошибок в консоли нет, изменения зафиксированы в репозитории. |
| 16–19 | Почти всё выполнено: блоки есть, но допущены 1–2 недочёта. Примеры каскада/порядка показаны, но объяснение в отчёте неполное; или архитектурное решение “компонент + модификатор” сделано, но местами остаются конфликтные/слишком специфичные селекторы; или border-box включён, но проверка ширины карточки 320px в DevTools не описана; или адаптив реализован, но breakpoint не доведён (карточка не полностью “в столбик”); или toggle работает, но состояние оформлено частично. В целом интерфейс рабочий и близок к требованиям. |
| 12–15 | Выполнено частично: присутствуют минимум 3–4 блока, но не все. Частая ситуация: есть Grid и Flex, но нет доказательства каскада (A) или нет архитектурного устранения конфликта; или есть border-box, но карточка 320px сделана неверно; или toggle реализован, но стили открытого состояния сделаны inline; или нет :focus-visible. Рабочий результат есть, но требования закрыты не полностью. |
| 8–11 | Есть базовая стилизация и отдельные элементы layout (например, только Flex или только Grid), но отсутствуют обязательные демонстрации: нет корректного блока (конфликт + доказательство), нет проверки box model, адаптив поверхностный, toggle/состояния не оформлены через классы, доступность не проверена. |
| 1–7 | Формальная работа: подключён CSS, но нет системной реализации заданий A–F. Стили “разрозненные”, каскад/специфичность не продемонстрированы, layout неустойчивый, адаптив отсутствует, JS toggle не работает или отсутствует, фокус не виден. |
| 0 | Работа не сдана / неработоспособна (страница не открывается, критические ошибки) / не соответствует требованиям оформления. |