CSS без угадывания. 16 разборов ошибок вёрстки

- -
- 100%
- +

Как работать с практикумом
Не свойство наугад, а проверяемая гипотеза
Знакомая ситуация: на широком экране всё почти хорошо, на узком край карточки исчезает. Хочется добавить overflow: hidden, ещё один width или !important. Иногда картинка становится аккуратнее, но причина остаётся: текст уже обрезан, кнопка недоступна, соседние блоки сжались. В этой книге мы будем сначала формулировать, что именно должно произойти, а затем проверять один механизм. Не каждый приведённый исходный вариант ошибочен сам по себе. Он намеренно не подходит к условию конкретной работы: например, растянутые карточки уместны в одном дизайне и нежелательны в другом.
Для начала нужны HTML-теги, классы, простые селекторы и понимание записи «свойство: значение». Устанавливать библиотеку, Node.js или сборщик не требуется. JavaScript в учебных файлах нет. Предполагается работа за компьютером: на телефоне можно читать объяснения, но изменять локальные HTML-файлы и пользоваться инструментами разработчика удобнее на большом экране. Книга не заменяет справочник по всем свойствам CSS и не охватывает доступность целого сайта.
Один файл — одна лабораторная работа
Создайте отдельную учебную папку. Для первой работы сохраните полный листинг как lab01.html, для следующей — как lab02.html и так далее. Используйте обычный редактор кода и кодировку UTF-8. Это должен быть текстовый файл, а не документ Word и не файл с двойным расширением .html.txt. Откройте его в браузере. После сохранения изменений обновляйте вкладку. Работайте только с этими учебными копиями, не заменяйте файлы настоящего проекта.
В книге полный листинг исходного файла даётся один раз для каждой работы. Блок «Найти в CSS» нужно заменить блоком «Заменить на» именно внутри style этого файла. Это замена, а не добавление второго противоречащего правила в конец. Упражнение начинается с уже исправленной версии и содержит свою отдельную замену. Возвращаться к исходному дефекту перед упражнением не нужно. Все изменения ограничены учебным CSS; разметка остаётся прежней.
Каждая строка кода напечатана отдельной строкой без номеров. Если читалка визуально переносит длинную строку, это не отдельная команда. При копировании проверьте угловые скобки, обычные кавычки, двоеточия и фигурные скобки. Не заменяйте кавычки на типографские. Отступы в этих листингах не определяют вложенность, однако пробел между частями селектора важен. Не соединяйте .layout .card в .layout.card: это уже другое условие выбора элемента.
Что именно считать шириной
В работах используйте ширину области страницы 320, 480 и 960 CSS-пикселей. Это ширина viewport, а не внешнего окна браузера со всеми панелями. В инструментах разработчика откройте режим адаптивного просмотра и введите нужное значение. Для повторения снимков оставьте масштаб страницы 100%, высоту области 720 и не подменяйте пользовательский агент мобильным устройством. Дополнительная ширина 650 нужна для последнего упражнения.
Во всех файлах у body есть по 16 пикселей отступа, а .stage занимает доступную ширину, но не более 560. Поэтому при viewport 320 область .stage имеет ширину 288, при 480 — 448, при 960 — 560. Это позволяет отличить свойство, зависящее от viewport, от размера самого блока. Некоторые дефекты заметны на всех трёх ширинах, другие — только на узкой. Нормальный результат широкого варианта не опровергает дефект узкого.
Общий шаблон намеренно не содержит универсального box-sizing: border-box и сброса всех стилей. Иначе часть механизмов была бы скрыта заранее. Собственные правила лабораторной работы записаны после двух общих правил. Системный шрифт Arial может выглядеть немного иначе в другой системе: сравнивайте прежде всего края, переносы, размеры и взаимное положение блоков, а не точное число окрашенных пикселей.
Как записать результат, которому можно доверять
Перед правкой сформулируйте наблюдение: «правый край больше правого края родителя», «ширина значка меньше 80», «между кнопкой и низом карточки остался свободный участок». Откройте Elements или «Элементы», выберите нужный id и изучите вычисленные стили и схему размеров. В режиме Grid или Flex браузер может показать линии и свободное место. Название панели зависит от браузера, но исходный HTML и CSS остаются одинаковыми.
Зафиксируйте исходное наблюдение, сделайте только указанную замену, обновите страницу и проверьте то же условие. Затем смените ширину. Когда результат неожиданен, сначала проверьте, тот ли файл открыт, сохранился ли он, совпадает ли селектор и не осталось ли двух разных версий правила. Не объявляйте механизм неверным только по старому снимку или вкладке, которую не обновили.
Снимки в книге — иллюстрации конкретной среды, а не обещание одинакового вида на каждом устройстве. Подробные условия и текстовый разбор остаются основной инструкцией. Автоматические проверки учебных вариантов проведены в Chromium; другие движки не проверялись. Совпадение на нескольких ширинах — полезный регрессионный тест, но не доказательство пригодности произвольной страницы для всех экранов и настроек.
Выбрать лабораторную работу
1. Сто процентов, которые шире родителя
2. Рисунок помнит свои шестьсот пикселей
3. Строка без единого места для переноса
4. Многоточие есть, а flex-строка всё равно распирается
5. Почему квадрат с width: 80px перестал быть квадратом
6. Три карточки требуют одну строку
7. Центр найден, но не по той оси
8. Кнопка должна уйти вниз, а заголовок — остаться наверху
9. Две доли Grid, которые не стали двумя равными колонками
10. Три колонки не обязаны помещаться на телефоне
11. Короткая карточка растянулась вслед за длинной
12. Карточка на всю строку создала лишнюю колонку
13. Уведомление сохранило высоту, но потеряло половину текста
14. Значок нашёл угол рабочей области вместо угла карточки
15. Сто процентов высоты без определённой высоты родителя
16. Мобильное правило включилось, но проиграло каскад
Лабораторные работы
Работа 1. Сто процентов, которые шире родителя
Цель и наблюдение
Научиться отличать ширину содержимого от наружной ширины блока. У карточки есть внутренние отступы и рамка, а справа она выходит за границу рабочей области. Требуется сохранить оформление, а не просто спрятать выступающий край.
Требования к учебному компонентуПосле исправления наружная ширина #card равна ширине #stage; их левые и правые границы совпадают. В этом примере у рабочей области нет собственной рамки или padding.
Внутренние отступы карточки остаются по 24 px, рамка — по 2 px. Текст виден целиком и может занимать несколько строк; фиксированная высота не требуется.
Проверяем окно шириной 320, 480 и 960 px. При стандартной оболочке соответствующие ширины рабочей области составляют 288, 448 и 560 px.
Перед правкойДо изменения CSS запишите прогноз: чему будет равна наружная ширина карточки в рабочей области 288 px? Не вычитайте отступы страницы второй раз: они уже учтены в размере #stage. Сначала выделите саму карточку в инспекторе, затем сравните её размеры с родителем.
В исходном варианте width: 100% задаёт 288 px только области содержимого. Слева и справа добавляются 24 px padding и 2 px рамки. Получается 340 px: выступание равно 52 px. Тот же избыток сохраняется при другой ширине родителя, хотя доля выступания выглядит меньше.
Замените значение box-sizing у карточки и повторите измерение. Теперь в заданные сто процентов входят содержимое, внутренние отступы и рамка. При ширине 288 px под текст остаётся 236 px. Нормально, если число строк увеличилось: наружный размер исправлен за счёт доступного пространства внутри.
lab01.html — полный исходный файл
Удобные поля не должны выталкивать карточку за край страницы.
Почему получается такой результат
Причина находится в модели размеров, а не в округлении процентов. Ошибка повторяется и на широком экране: у карточки сохраняется лишних 52 px относительно родителя. Сначала сравнивайте рамки компонентов; полоса прокрутки страницы — лишь возможное внешнее последствие.
Правка локальна: она меняет смысл width именно у .card. Свойство box-sizing не наследуется само по себе, поэтому соседние элементы от этой декларации не переходят на другую модель. Здесь намеренно нет универсального сброса, который заранее скрыл бы причину.
width: calc(100% - 52px) тоже может дать нужную геометрию для исходных чисел, но связывает ширину с двумя другими настройками. Если дизайнер увеличит padding, придётся помнить о второй правке. В этой задаче border-box точнее выражает договор о наружной ширине.
overflow: hidden на странице не делает карточку уже. Оно только ограничивает видимость выступающей части и способно скрыть текст или контур фокуса. Равенство границ должно получиться без обрезания родителя; проверьте его до оценки красоты результата.
Найти в CSS
box-sizing: content-box;
Заменить на
box-sizing: border-box;
Проверить исправление
Сохраните файл, обновите страницу и повторите условия проверки на ширинах 320, 480 и 960 CSS-пикселей. Ниже показан узкий вариант до и после правки. Снимок может обрезать содержимое, выходящее за viewport: это часть демонстрируемого дефекта, а не способ исправления.
До правки: viewport320

border-box не включает внешние margin и не запрещает потомкам переполнять блок. Если сами padding и рамки больше доступной ширины, модель не создаст отрицательную область содержимого. Эта правка решает конкретный конфликт width с внутренними добавками, а не любой горизонтальный выход.
Самостоятельное изменение
В исправленном варианте увеличьте padding карточки с 24 до 40 px. До запуска предскажите наружную ширину и ширину области содержимого при окне 320 px. Нужно ли менять width?
Ответ к работе 1
К списку работ
Работа 2. Рисунок помнит свои шестьсот пикселей
Цель и наблюдение
Ограничить большой рисунок шириной контейнера и сохранить пропорции. Иллюстрация здесь авторская: три фигуры во встроенном SVG. Она не загружается из сети, поэтому наблюдаемый результат не зависит от доступа к серверу или готовности внешнего файла.
Требования к учебному компонентуНаружная ширина #diagram не должна превышать ширину #stage. Рисунок выравнивается по левому краю рабочей области и остаётся полностью видимым без горизонтальной обрезки.
Отношение ширины рисунка к высоте остаётся 3:1. Круг должен оставаться кругом, а не становиться овалом; правая фигура не должна исчезать за краем.
Оригинальный размер задан атрибутами SVG: 600 × 200. Ограничение не должно заставлять рисунок обязательно растягиваться сверх исходной ширины, если его позже перенесут в более широкий контейнер.
Перед правкойОткройте исходник на узком экране и найдите правую фиолетовую фигуру. В SVG задана ширина 600 px, тогда как рабочая область при окне 320 px занимает 288 px. Сопоставьте размеры самого SVG и #stage: нельзя считать рисунок адаптивным только потому, что его родитель имеет width: 100%.
Вставьте две связанные декларации: max-width: 100% и height: auto. Первая ограничивает размер сверху доступной шириной. Вторая отменяет фиксированную CSS-высоту 200 px и позволяет высоте следовать пропорции рисунка. Координатная область viewBox остаётся прежней — редактировать точки фигур не требуется.
Проверьте обе величины. Для ширины 288 px ожидается высота 96 px; для 448 px — примерно 149,33 px; для 560 px — примерно 186,67 px. Дробные CSS-пиксели допустимы. Сравните не только числа, но и вид круга: геометрия помогает заметить потерю пропорций.
lab02.html — полный исходный файл
Три фигуры должны оставаться видимыми целиком.
Почему получается такой результат
Процентная ширина родителя не переписывает размеры всех вложенных объектов. У рисунка есть собственные исходные размеры. Поэтому искать ошибку нужно на #diagram, а не добавлять ещё один контейнер со столь же адаптивной шириной.
max-width задаёт потолок, а не обязательную ширину. Для этого SVG с исходными 600 px правило разрешает уменьшение до ширины родителя. Само по себе оно не означает «займи всю свободную строку при любых исходных размерах». Это отличие важно для небольших значков.
Если оставить height: 200px, CSS-прямоугольник рисунка не сохранит ожидаемое отношение 3:1. Внутренний viewBox может вписать фигуры с незаполненным пространством, но это не исправляет неверную высоту самого элемента. Проверка рамки SVG надёжнее, чем один взгляд на его содержимое.
Уменьшение через transform: scale(...) меняет визуальное отображение, но не заменяет нужное здесь управление размером в потоке. Обрезание через overflow тоже прячет симптом. Договор требует, чтобы прямоугольник рисунка действительно помещался, а подпись следовала сразу за ним.
Найти в CSS
max-width: none;
height: 200px;
Заменить на
max-width: 100%;
height: auto;
Проверить исправление
Сохраните файл, обновите страницу и повторите условия проверки на ширинах 320, 480 и 960 CSS-пикселей. Ниже показан узкий вариант до и после правки. Снимок может обрезать содержимое, выходящее за viewport: это часть демонстрируемого дефекта, а не способ исправления.
До правки: viewport320

Пример относится к SVG с заданным viewBox и известной пропорцией. Для CSS-фона, обрезаемой обложки или изображения в рамке фиксированной высоты договор будет другим. Поля и рамку самого рисунка тоже нужно учитывать отдельно; здесь их нет, чтобы изолировать ограничение размера.
Самостоятельное изменение
В исправленном варианте задайте рисунку width: 80%, сохранив max-width: 100% и height: auto. Предскажите его ширину и высоту при рабочей области 288 px. Должен ли SVG остаться по центру?
Ответ к работе 2
К списку работ
Работа 3. Строка без единого места для переноса
Цель и наблюдение
Исправить выход длинного идентификатора за рамку обычного блочного элемента. В этой лабораторной нет flex и grid: ширина блока уже верна. Нужно научить текст переноситься, сохранив все символы и не добавляя пробелы в исходное значение.
Требования к учебному компонентуРамка #label помещается в #stage до и после исправления. После правки также должно помещаться содержимое: scrollWidth у #label не превышает clientWidth более чем на погрешность округления в один пиксель.
Идентификатор остаётся в DOM одной исходной строкой. Допустимы визуальные переносы между символами, но недопустимы удаление конца, многоточие или вставка пробелов ради подгонки.
Высота блока свободна. На узком экране он занимает больше строк, а на широком — меньше; все строки находятся внутри цветной области.
Перед правкойСравните два объекта: внешний прямоугольник #label и нарисованный текст. Обычный блок с автоматической шириной уже учитывает свои padding и рамку в доступном месте. Здесь выступает не весь блок, а длинная последовательность внутри него. Исправление из первой лабораторной не устраняет отсутствие мест переноса.
В исходной строке намеренно нет пробелов и дефисов. При overflow-wrap: normal браузеру не предложена подходящая обычная точка разрыва этой последовательности. Уменьшайте окно постепенно и следите за правым краем рамки: он движется, а строка продолжает тянуться наружу.
Замените normal на anywhere. Когда неразрывный фрагмент перестаёт помещаться, браузер получает возможность перенести его между символами. Исходный текст при этом не редактируется, дефис автоматически не добавляется. Проверьте конец строки и рост высоты блока на всех трёх ширинах.
lab03.html — полный исходный файл
ALPHA0123456789BETA0123456789GAMMA0123456789DELTA0123456789OMEGA0123456789END
Почему получается такой результат
Полезная диагностическая пара — clientWidth и scrollWidth. Первая величина описывает внутреннюю ширину с padding, вторая учитывает необходимую ширину содержимого. До правки вторая заметно больше первой, хотя наружная рамка не шире родителя. После переноса горизонтальный избыток должен исчезнуть.
Свойство overflow-wrap решает задачу текста, а не внешней геометрии компонента. Оно не сожмёт слишком широкий SVG и не отменит фиксированную ширину соседней карточки. Сначала установите, какая именно часть выступает, и только затем выбирайте семейство свойств.
Уменьшить шрифт до размера, при котором строка случайно помещается, — плохая подмена договора. Новый идентификатор может быть длиннее, а читателю может понадобиться крупный текст. Исправленный блок должен становиться выше, сохраняя заданную читаемость.
overflow: hidden скрывает хвост, а text-overflow: ellipsis предназначен для другого решения: показать сокращённую подпись. В нашей задаче требуется весь идентификатор. Перенос допустим только потому, что это прямо записано в договоре, а не потому, что всегда предпочтительнее многоточия.
Найти в CSS
overflow-wrap: normal;
Заменить на
overflow-wrap: anywhere;
Проверить исправление
Сохраните файл, обновите страницу и повторите условия проверки на ширинах 320, 480 и 960 CSS-пикселей. Ниже показан узкий вариант до и после правки. Снимок может обрезать содержимое, выходящее за viewport: это часть демонстрируемого дефекта, а не способ исправления.
До правки: viewport320

anywhere не является переносом по правилам русского языка и не вставляет словарные дефисы. Для прозы сначала важны обычные переносы слов и подходящий язык документа. Если white-space запрещает перенос строк, сначала нужно пересмотреть этот запрет; данная лабораторная явно использует normal.
Самостоятельное изменение
Увеличьте font-size с 16 до 20 px в исправленном примере, оставив line-height: 1.5. Какие размеры обязаны сохраниться, какие могут измениться и какое измерение выявит возврат горизонтальной ошибки?
Ответ к работе 3
К списку работ
Конец ознакомительного фрагмента.
Текст предоставлен ООО «Литрес».
Прочитайте эту книгу целиком, купив полную легальную версию на Литрес.



