Одна страница для телефона, планшета и компьютера — и как выложить её в интернет
meta viewport, и видеть, что бывает без неёmin-widthБольше половины посетителей сайтов приходят с телефонов. Исторически сайты верстали под компьютер, и мобильные браузеры, чтобы показать их целиком, считают страницу без указаний настольной — шириной 980px — и уменьшают. Строка в <head> отменяет это поведение:
<meta name="viewport" content="width=device-width, initial-scale=1">
Две одинаковые страницы на экране телефона шириной 390px: сначала без этой строки (снято в окне 980px и уменьшено так, как это делает мобильный браузер), затем с ней:


Без viewport медиазапросы для телефонов не сработают вовсе: браузер считает ширину 980px. Поэтому эта строка — в скелете документа с модуля 01.
Медиазапрос @media (условие) { … } применяет правила внутри только при выполнении условия — чаще всего при ширине окна. Подход mobile first: основные стили пишут для телефона (одна колонка, всё друг под другом), а для широких экранов добавляют правила через min-width:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mobile first</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font-family: Arial, sans-serif; line-height: 1.4; }
.header { padding: 12px 16px; background: #1967d2; color: white; }
.header nav { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 6px; }
.header a { color: white; }
.wrap { max-width: 1100px; margin: 0 auto; padding: 16px; }
h1 { font-size: clamp(1.5rem, 4vw + 0.5rem, 2.5rem); margin: 0 0 12px; }
.cards { display: grid; grid-template-columns: 1fr; gap: 12px; }
.card { padding: 12px; border: 1px solid #ddd; border-radius: 8px; }
.card h2 { margin: 0 0 4px; font-size: 1.1rem; }
.card p { margin: 0; }
/* Планшет и шире */
@media (min-width: 600px) {
.header { display: flex; justify-content: space-between; align-items: center; }
.header nav { margin-top: 0; }
.cards { grid-template-columns: repeat(2, 1fr); }
}
/* Компьютер */
@media (min-width: 900px) {
.cards { grid-template-columns: repeat(3, 1fr); }
}
</style>
</head>
<body>
<header class="header">
<strong>Кафе «Нева»</strong>
<nav>
<a href="#">Меню</a>
<a href="#">Бронь</a>
<a href="#">Контакты</a>
</nav>
</header>
<main class="wrap">
<h1>Завтраки весь день</h1>
<div class="cards">
<article class="card"><h2>Сырники</h2><p>Со сметаной и вареньем</p></article>
<article class="card"><h2>Омлет</h2><p>С сыром и зеленью</p></article>
<article class="card"><h2>Каша</h2><p>Овсяная с ягодами</p></article>
</div>
</main>
</body>
</html>


Почему от телефона, а не наоборот: мобильная раскладка проще (одна колонка), и телефону не приходится загружать и отменять правила настольной версии. Точки перехода (600px, 900px) выбирают не по моделям устройств, а там, где раскладка начинает выглядеть плохо: растяните окно и посмотрите, на какой ширине строки становятся слишком длинными или карточки — слишком широкими.
| Условие | Когда срабатывает |
|---|---|
(min-width: 600px) | Окно шириной 600px и больше |
(max-width: 599px) | Окно уже 600px — для подхода «от компьютера» |
(orientation: landscape) | Ширина больше высоты |
(prefers-color-scheme: dark) | В системе включена тёмная тема |
(prefers-reduced-motion: reduce) | Пользователь просил меньше анимации |
print | Печать страницы |
Адаптивность — это не только медиазапросы. Многое подстраивается само, если не задавать жёстких размеров:
max-width вместо width у колонок и блоков.fr и сетка repeat(auto-fit, minmax(…, 1fr)) меняют число колонок без медиазапросов.flex-wrap: wrap переносит пункты меню, когда они не помещаются (так в шапке примера выше).clamp(минимум, желаемое, максимум) — значение, которое растёт с шириной окна в заданных границах. В примере заголовок: clamp(1.5rem, 4vw + 0.5rem, 2.5rem) — на телефоне 24px, на широком экране не больше 40px.rem, шрифты в rem — уважают настройку размера шрифта пользователя.width в пикселях шире экрана, картинка без max-width, длинный адрес или слово без переноса, таблица. Найти виновника: F12 → режим устройства, затем по очереди выделять крупные блоки и смотреть, какой выходит за правый край.Картинка шире экрана — самая частая причина горизонтальной прокрутки. Одна строка CSS ограничивает её шириной родителя, а height: auto сохраняет пропорции:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Картинки</title>
<style>
body { font-family: Arial, sans-serif; margin: 16px; }
img { max-width: 100%; height: auto; }
</style>
</head>
<body>
<img src="wide.svg" alt="Панорама набережной, схематично" width="900" height="300">
<p>Картинка шириной 900px уменьшается до ширины экрана и сохраняет пропорции.</p>
</body>
</html>

Но уменьшенная картинка всё равно весит, как большая: телефон загружает файл для широкого экрана. Атрибуты srcset и sizes дают браузеру несколько вариантов, и он выбирает подходящий по ширине экрана и плотности пикселей; <picture> позволяет предложить современный формат с запасным вариантом:
<img src="hall-800.jpg"
srcset="hall-400.jpg 400w, hall-800.jpg 800w, hall-1600.jpg 1600w"
sizes="(min-width: 900px) 800px, 100vw"
alt="Зал кафе с окнами на Неву" width="800" height="533" loading="lazy">
<picture>
<source srcset="hall.avif" type="image/avif">
<source srcset="hall.webp" type="image/webp">
<img src="hall.jpg" alt="Зал кафе с окнами на Неву" width="800" height="533">
</picture>
400w — реальная ширина файла в пикселях; sizes — какую ширину картинка займёт на странице. loading="lazy" откладывает загрузку картинок ниже первого экрана до прокрутки к ним.
Режим устройства в инструментах разработчика (Ctrl+Shift+M) показывает страницу в размерах популярных телефонов и позволяет тянуть ширину мышью. Это быстро, но не заменяет настоящий телефон: там другие шрифты, «чёлка» экрана, пальцы вместо курсора.
outline, дайте свою через :focus-visible.Открыть страницу на телефоне можно до публикации: запустите в каталоге сайта простой веб-сервер python3 -m http.server 8000 (курс «Основы Python», в Windows — py -m http.server 8000) и откройте на телефоне, подключённом к той же сети Wi-Fi, адрес http://IP-адрес-компьютера:8000. Брандмауэр Windows при первом запуске спросит разрешение на доступ из сети.
index.html в корне; имена файлов и каталогов — латиница в нижнем регистре, без пробелов.Logo.png и logo.png).<title> и <meta name="description"> — их показывает поисковик.<link rel="icon" href="favicon.svg">.Сайт из HTML, CSS и картинок — статический: серверу нужно только отдавать файлы. Варианты, от простого к сложному:
| Вариант | Как | Что нужно знать |
|---|---|---|
| Хостинг статических сайтов или страниц из git-репозитория | Загрузить файлы или отправить репозиторий; сервис выдаёт адрес | Бесплатно, HTTPS сразу; доступность сервиса из России проверяйте на момент публикации |
| Виртуальный хостинг российского провайдера | Загрузить файлы в каталог сайта по SFTP или через панель | Свой домен, HTTPS через панель; платно, недорого |
| Свой сервер (VPS) с nginx | Скопировать файлы в каталог, настроить nginx | Полный контроль; администрирование — на вас (курс «Основы Linux») |
Минимальная настройка nginx для статического сайта в Debian — файл /etc/nginx/sites-available/cafe:
server {
listen 80;
server_name cafe.example.ru;
root /var/www/cafe;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
Затем ссылка в sites-enabled, проверка sudo nginx -t и sudo systemctl reload nginx. Сертификат HTTPS бесплатно выдаёт Let's Encrypt, программа certbot получает и продлевает его сама. Без HTTPS браузеры помечают сайт как небезопасный, а формы с данными пользователей без него недопустимы (модуль 03).
meta viewport телефон показывает уменьшенную настольную страницу, и медиазапросы не работают@media (min-width: …)img { max-width: 100%; height: auto; }; srcset — чтобы телефон не грузил большие файлы