Files
wow-design-wiki/mobile/mobile-improvements-case-study.md

403 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Кейс-стади: Мобильная оптимизация лендинга Никиты Тарасова
> Production-кейс: лендинг ~2300 строк, 100KB, одиночный HTML-файл → улучшенная мобильная версия. 20+ мобильных улучшений.
## Исходные данные
- **Тип:** Персональный лендинг (один HTML-файл, без сборщика)
- **Объём:** ~2325 строк, ~101KB
- **Стек:** Чистый HTML + CSS + минимальный vanilla JS, Tailwind CDN
- **Проблема:** Базовые `@media (max-width: 768px)` медиа-запросы, нет safe-area, нет sticky CTA, нет prefers-reduced-motion, нет touch-оптимизаций
## Категории улучшений
### 1. 🏠 Viewport & Safe Area (iOS)
#### Проблема: `100vh` обрезает контент на iOS
На iPhone Safari `100vh` включает область адресной строки, которая при скролле сжимается. Контент внизу обрезается.
**Исправление:**
```html
<!-- Было -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Стало -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
```
```css
/* Для секции hero */
#hero {
min-height: 100dvh; /* dynamic viewport height — учитывает/не учитывает UI браузера */
}
/* Для контейнера — safe-area */
@supports (padding: max(0px)) {
.container {
padding-left: max(24px, env(safe-area-inset-left));
padding-right: max(24px, env(safe-area-inset-right));
}
body {
padding-bottom: max(0px, env(safe-area-inset-bottom));
}
}
/* Для фиксированной навигации */
.nav {
padding-top: max(0px, env(safe-area-inset-top));
}
```
**Зачем:** Без `viewport-fit=cover` safe-area инсеты игнорируются. Без `dvh` на iPhone 15 Pro с Dynamic Island hero-секция обрезается на ~50px.
---
### 2. 📱 Sticky Bottom CTA (мобильная конверсия)
#### Проблема: на мобильном CTA «внизу» скрыты в конце длинной страницы. Пользователь должен пролистать обратно.
**Исправление:**
```css
.sticky-cta {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 12px 16px;
padding-bottom: max(12px, env(safe-area-inset-bottom));
background: rgba(10, 10, 11, 0.95);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-top: 1px solid var(--border);
z-index: 90;
display: none; /* desktop: hidden */
flex-direction: column;
gap: 4px;
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.3);
}
/* Показываем только на мобильных */
@media (max-width: 768px) {
.sticky-cta { display: flex; }
main { padding-bottom: 80px; } /* место под sticky CTA */
}
```
```html
<div class="sticky-cta">
<a href="#contact" class="btn btn-primary sticky-cta__btn">Обсудить проект</a>
<span class="sticky-cta__hint">Средний отклик — менее 2 часов</span>
</div>
```
**Зачем:** Sticky CTA на мобильном повышает конверсию на 15-30% (источники: Baymard Institute, CXL). Пользователь всегда видит кнопку действия, не нужно скроллить.
---
### 3. 👆 Touch Targets (WCAG 2.5.5)
#### Проблема: кнопки мобильного меню и ссылки имеют произвольный размер. На телефоне с пальцем трудно попасть.
**Исправление:**
```css
:root {
--touch-min: 44px; /* минимум по WCAG 2.5.5 и Apple HIG */
}
.mobile-menu-btn {
min-width: var(--touch-min);
min-height: var(--touch-min);
display: inline-flex;
align-items: center;
justify-content: center;
}
.mobile-menu a {
min-height: var(--touch-min);
transition: color 0.2s;
}
.mobile-menu a:active {
background: var(--accent-dim);
}
```
**Зачем:** Apple HIG требует 44×44pt, WCAG 2.5.5 — 44×44px. Меньше — палец не попадает, пользователь уходит.
---
### 4. 🎯 Touch Feedback
#### Проблема: на десктопе hover-эффекты, на мобильном — ничего. Пользователь не понимает, что нажал.
**Исправление:**
```css
.btn {
/* Отключаем тёмный фон при тапе на Chrome/Android */
-webkit-tap-highlight-color: transparent;
will-change: transform;
}
/* Active state для мобильных */
.btn:active {
transform: scale(0.97); /* микро-сжатие как в нативных приложениях */
}
.sticky-cta__btn:active {
transform: scale(0.98);
}
```
**Зачем:** Визуальная обратная связь при тапе критична на тач-устройствах. Scale 0.97 создаёт ощущение «нажатия» без задержки.
---
### 5. 📦 Font Loading (FOIT/FOUT)
#### Проблема: Google Fonts загружается асинхронно, текст появляется сначала system font, потом Inter → видимый «скачок» шрифта.
**Исправление:**
```html
<!-- Preload CSS-ресурса -->
<link rel="preload" href="https://fonts.googleapis.com/css2/..." as="style" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2/..." />
```
**Зачем:** `preload` сообщает браузеру приоритет загрузки шрифта. Без него шрифт может загрузиться через 2-3 секунды.
---
### 6. ♿ Accessibility
#### Предпочтение reduced motion
```css
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
.reveal { opacity: 1; transform: none; }
}
```
**Зачем:** ~15% пользователей имеют включённый reduced motion (вестибулярные нарушения, мигрень). Игнорирование `prefers-reduced-motion` — нарушение WCAG 2.2.1.
#### iOS Bounce (overscroll-behavior)
```css
body {
overscroll-behavior-y: contain;
}
.mobile-menu {
overflow-y: auto;
overscroll-behavior-y: contain;
}
```
**Зачем:** На iOS Safari `overscroll-behavior` по умолчанию `auto` создаёт пружинящий эффект, который ломает ощущение «приложения». `contain` ограничивает bounce внутри элемента.
#### Backdrop-filter для всех браузеров
```css
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px); /* Safari 9-15 */
```
**Зачем:** `-webkit-` префикс нужен для Safari на iOS 15 и младше. Без него blur не работает.
---
### 7. 📐 Layout on Mobile
#### Trust Bar 4-колонка → 2×2
```css
@media (max-width: 768px) {
.trust-grid {
grid-template-columns: repeat(2, 1fr) !important;
}
}
```
#### Hero-секция: `100dvh` вместо `100vh`
```css
@media (max-width: 768px) {
#hero {
min-height: 100dvh;
}
}
```
#### Фото hero на мобильном — ограниченный размер
```css
@media (max-width: 768px) {
.photo-hero-mobile {
max-height: 280px;
aspect-ratio: 4/5;
}
}
```
**Зачем:** Полноразмерное фото 600×900 на мобильном занимает весь экран. Ограничение до 280px оставляет место для CTA над фото.
#### Контейнер контактов: CTA вертикально
```css
@media (max-width: 768px) {
.cta-stack {
flex-direction: column !important;
}
.cta-stack .btn {
width: 100%;
}
}
```
---
### 8. 🍔 Мобильное меню
#### Close button внутри
```html
<button class="mobile-menu-close" aria-label="Закрыть меню" onclick="...">✕</button>
```
#### Emoji иконки для навигации
```html
<a href="#services">📊 Услуги</a>
<a href="#products">📦 Продукты</a>
<a href="#cases">🏆 Кейсы</a>
<a href="#about">👤 Обо мне</a>
```
**Зачем:** На мобильном нет hover. Emoji даёт визуальный якорь. Close button в правом верхнем углу — стандартный паттерн iOS.
---
## Сводная таблица всех изменений
| # | Категория | Изменение | Приоритет | Impact |
|---|-----------|-----------|-----------|--------|
| 1 | Viewport | `viewport-fit=cover` | 🔴 Critical | iOS safe-area |
| 2 | Viewport | `100dvh` для hero | 🔴 Critical | iOS контент |
| 3 | Layout | `env(safe-area-inset-*)` | 🔴 Critical | iPhone notch/Dynamic Island |
| 4 | CTA | Sticky bottom CTA | 🟡 High | +15-30% конверсия |
| 5 | Touch | 44×44px минимум | 🟡 High | WCAG 2.5.5 |
| 6 | Touch | `:active` feedback | 🟡 High | Пользовательский опыт |
| 7 | Touch | `-webkit-tap-highlight-color` | 🟡 High | UX на Android |
| 8 | Perf | Font preload | 🟡 High | Убирает FOIT |
| 9 | A11y | `prefers-reduced-motion` | 🟢 Medium | 15% пользователей |
| 10 | A11y | `overscroll-behavior-y` | 🟢 Medium | iOS bounce |
| 11 | A11y | `-webkit-backdrop-filter` | 🟢 Medium | Safari iOS |
| 12 | Layout | Hero photo limit | 🟢 Medium | Компоновка |
| 13 | Layout | Trust bar 2×2 | 🟢 Medium | Компактность |
| 14 | Layout | CTA vertical stack | 🟢 Medium | Touch-удобство |
| 15 | Layout | Container safe padding | 🔴 Critical | Контент не обрезан |
| 16 | Layout | `main` bottom padding | 🟡 High | Sticky CTA не перекрывает |
| 17 | Nav | Close button mobile | 🟡 High | Мобильное меню |
| 18 | Nav | Emoji icons | 🟢 Low | Мобильная навигация |
| 19 | Nav | `padding-top` safe-area | 🟡 High | Навигация не обрезана |
| 20 | Nav | `overflow-y: auto` menu | 🟢 Medium | Scroll внутри меню |
---
## Антипаттерны, которых стоит избегать
### ❌ `100vh` вместо `100dvh`
```css
/* БУДЬТЕ ОСТОРОЖНЫ — iOS Safari */
.hero { min-height: 100vh; } /* ← контент обрезан */
.hero { min-height: 100dvh; } /* ← правильно */
```
### ❌ Фиксированная позиция без safe-area
```css
/* БУДЬТЕ ОСТОРОЖНЫ — iPhone X+ */
.footer { bottom: 0; } /* ← под home indicator */
.footer { bottom: env(safe-area-inset-bottom); } /* ← правильно */
```
### ❌ Touch-элементы меньше 44px
```css
/* БУДЬТЕ ОСТОРОЖНЫ */
.icon-btn { width: 32px; height: 32px; } /* ← палец не попадёт */
.icon-btn { min-width: 44px; min-height: 44px; } /* ← правильно */
```
### ❌ Скрытие hover вместо адаптации
```css
/* БУДЬТЕ ОСТОРОЖНЫ */
.btn:hover { background: gold; } /* ← на тач-экранах не работает */
.btn:active { background: gold; } /* ← добавьте для тач */
```
### ❌ Фиксированные размеры для шрифтов
```css
/* БУДЬТЕ ОСТОРОЖНЫ */
h1 { font-size: 2.5rem; } /* ← одинаковый на всех экранах */
h1 { font-size: clamp(2rem, 6vw, 3.5rem); } /* ← адаптивный */
```
---
## Чек-лист тестирования
### Обязательные тесты:
- [ ] **iPhone 15 Pro** (Dynamic Island) — Safari
- [ ] **iPhone SE** (маленький экран) — Safari
- [ ] **Android (Pixel)** — Chrome
- [ ] **iPad** — Safari
- [ ] **Landscape** — Safari, Chrome
- [ ] **Reduced motion** — Settings > Accessibility
- [ ] **Dark mode** — системные настройки
### Инструменты:
1. **Chrome DevTools** — Device Toolbar (Ctrl+Shift+M)
2. **Safari Web Inspector** — Remote debugging на iPhone
3. **Lighthouse** — Performance, Accessibility, Mobile-friendly
4. **WebPageTest** — реальные тесты с реальных устройств
5. **BrowserStack** — реальные устройства, не только эмуляция
### Метрики:
- **LCP** (Largest Contentful Paint) < 2.5s на мобильном
- **INP** (Interaction to Next Paint) < 200ms
- **CLS** (Cumulative Layout Shift) < 0.1
- **Touch targets** ≥ 44×44px
- **Font loading** < 1s (FOIT невидимый)
---
## Итог
20 изменений, которые превратили десктопный лендинг в полноценное мобильное приложение:
1. **Safe area** — контент не обрезан на iPhone с notch/Dynamic Island
2. **Sticky CTA** — кнопка действия всегда видна → рост конверсии
3. **Touch targets** — 44px минимум по стандартам Apple/Google
4. **Touch feedback** — `:active` scale вместо ненавидимого hover
5. **dvh** — hero занимает весь экран без обрезки
6. **Accessibility** — reduced-motion, overscroll, WCAG
7. **Performance** — preload шрифтов
**Общий размер файла:** +8KB (было 101KB, стало 109KB)
---
## Источники
- [MDN: CSS env() function](https://developer.mozilla.org/en-US/docs/Web/CSS/env())
- [MDN: dvh unit](https://developer.mozilla.org/en-US/docs/Web/CSS/viewport-percentage-lengths)
- [Apple Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines)
- [WCAG 2.1: Target Size](https://www.w3.org/WAI/WCAG21/Understanding/target-size.html)
- [Google Core Web Vitals](https://web.dev/vitals/)
- [Baymard: Mobile Navigation](https://baymard.com/blog/mobile-navigation)