На макете три аккуратные карточки, а на телефоне появился горизонтальный скролл. Вы меняете ширину контейнера, уменьшаете gap и ставите overflow-x: hidden. Визуально проблема пропала, но часть адреса или примера кода стала недоступна. Чтобы исправить причину, нужно разделить ширину трека, минимальную ширину элемента и поведение его содержимого.
Почему колонка 1fr может оказаться слишком широкой
Доля fr участвует в распределении свободного пространства. Но одиночная запись 1fr имеет автоматическую минимальную границу: она не равна обещанию «помести сюда любой текст». Длинное неразрывное слово, фиксированная картинка или блок pre могут влиять на минимальную ширину. В результате трек не сжимается до ожидаемой доли, даже если внешнему контейнеру задана ширина 100%.
Используйте DevTools, чтобы найти конкретный блок за правой границей. Временно включите outline для элементов, посмотрите вычисленные min-width и grid-template-columns. Проверьте, не находится ли внутри вложенный flex-контейнер с собственной автоматической минимальной шириной. Размером управляет вся цепочка родителей, поэтому одна правка на самом глубоком слове не всегда достаточна.
Сетка карточек, которая помещается в узкий контейнер
Ниже самостоятельный пример. Размер 18rem задаёт желательную нижнюю границу карточки. min(100%, 18rem) позволяет уменьшить её, когда контейнер уже этого значения. auto-fit схлопывает свободные треки, а 1fr распределяет оставшееся место между существующими карточками. Это подходит для однотипного списка; для главного материала и боковой панели лучше описать отдельную композицию.
TSX<section class="catalog" aria-label="Учебные материалы">
<article class="item"><h2>React и запросы</h2><p>Отмена и устаревшие ответы.</p></article>
<article class="item"><h2>TypeScript и API</h2><p class="long">very-long-unbroken-api-response-property-name-for-layout-testing</p></article>
<article class="item"><h2>Пример кода</h2><pre tabindex="0" role="region" aria-label="Длинный пример"><code>const address = 'https://example.test/one/very/long/path/without/line/breaks';</code></pre></article>
</section>
JavaScript/* Скопируйте вместе с HTML выше в пустую страницу */
* { box-sizing: border-box; }
body { margin: 0; padding: 1rem; font-family: system-ui; }
.catalog {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
gap: 1rem;
}
.item { min-width: 0; padding: 1rem; border: 1px solid #64748b; }
.item h2, .long { overflow-wrap: anywhere; }
.item pre { max-width: 100%; overflow-x: auto; padding: 1rem; background: #eef2ff; }
.item img { max-width: 100%; height: auto; }
:focus-visible { outline: 3px solid #2145db; outline-offset: 3px; }
Когда нужна запись minmax(0, 1fr)
У двух областей разной важности, например статьи и оглавления, можно явно задать grid-template-columns: minmax(0, 1fr) 18rem. Основная колонка получает доступное пространство без автоматической нижней границы трека. При уменьшении экрана переведите обе области в одну колонку. min-width: 0 на дочернем элементе остаётся полезным: он описывает минимальную ширину самого элемента, а не размер трека.
Выберите, что переносить, а что прокручивать
Заголовок или URL обычно можно перенести. overflow-wrap: anywhere разрешает разрыв длинной последовательности при нехватке места и влияет на расчёт min-content. Пример кода сохраняет пробелы и структуру: здесь внутренний горизонтальный скролл может быть понятнее произвольных переносов. Не превращайте всю страницу в прокручиваемое полотно только ради одной длинной строки.
Прокручиваемый блок должен быть достижим с клавиатуры. В примере pre получил tabindex="0" и доступное имя. Видимый фокус помогает понять, куда попадут нажатия стрелок. Эти атрибуты полезны для проверки браузеров, в которых scroll-контейнер не становится фокусируемым автоматически. Не добавляйте tabindex к каждому абзацу: это делает обычное чтение неудобным.
Как проверить адаптивность после исправления
- Откройте пример при ширине 320, 375, 768 и 1440 пикселей. Сравните scrollWidth корневого элемента с шириной viewport.
- Подставьте очень длинный заголовок и URL без пробелов. Добавьте карточку с картинкой и проверьте её реальную ширину.
- Увеличьте масштаб браузера до 200%, затем проверьте основной сценарий. При 400% оцените переход в одну колонку и сохранность текста.
- Пройдите Tab до примера кода. Убедитесь, что фокус виден и содержимое можно прокрутить, не сдвигая всю страницу.
- Проверьте, что изменение не обрезало меню, выпадающий список и индикатор фокуса. Скриншот только первого экрана этого не доказывает.
Измерение scrollWidth полезно как быстрый сигнал, но не заменяет чтение страницы. Скрытый overflow может дать хороший результат измерения и одновременно потерять содержимое. Тест должен проверить не только отсутствие скролла, но и доступность длинного примера. Поэтому сначала выбирайте ожидаемое поведение, затем фиксируйте его автоматической проверкой.
Практика и первичные источники
Примените подход к своему каталогу, затем проверьте доступность формы поиска и скорость интерфейса. Для обсуждения проекта сохраните скриншот до исправления, CSS и объяснение причины; критерии ревью есть в практикуме.
Определения размеров трека: MDN - minmax. Автоматическая минимальная ширина: MDN - min-width. Перенос содержимого: MDN - overflow-wrap. Пример показывает сетку материалов, а не универсальную систему компоновки любого приложения.