diff --git a/mobile/mobile-improvements-case-study.md b/mobile/mobile-improvements-case-study.md new file mode 100644 index 0000000..e72d5a3 --- /dev/null +++ b/mobile/mobile-improvements-case-study.md @@ -0,0 +1,402 @@ +# Кейс-стади: Мобильная оптимизация лендинга Никиты Тарасова + +> 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 + + + + + +``` + +```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 +
+ Обсудить проект + Средний отклик — менее 2 часов +
+``` + +**Зачем:** 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` сообщает браузеру приоритет загрузки шрифта. Без него шрифт может загрузиться через 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 + +``` + +#### Emoji иконки для навигации + +```html +📊 Услуги +📦 Продукты +🏆 Кейсы +👤 Обо мне +``` + +**Зачем:** На мобильном нет 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)