← HTML & CSS/Медиа-запросы и адаптивность#27 из 383← ПредыдущийСледующий →+20 XP
Полезно по теме:Гайд: старт в frontendПрактика: DOM и событияТермин: DOMМаршрут: старт с нуля

Медиа-запросы и адаптивность

Больше половины трафика в интернете приходит с мобильных. Сайт, который не адаптируется под экраны — это сайт, который теряет половину пользователей. Медиа-запросы позволяют применять разные стили в зависимости от ширины экрана, ориентации устройства и других характеристик.

Что такое медиа-запрос

@media — это условный оператор в CSS. Если условие выполняется, стили внутри применяются.

@media (max-width: 768px) {
  /* Эти стили активны только на экранах шириной ≤ 768px */
  .nav { display: none; }
  .hamburger { display: block; }
}

Синтаксис

@media тип_медиа и (условие) {
  /* CSS-правила */
}

/* Примеры */
@media screen and (max-width: 768px) { }   /* Только экраны, ≤768px */
@media (min-width: 1024px) { }             /* ≥1024px (тип screen подразумевается) */
@media (min-width: 768px) and (max-width: 1023px) { }  /* Диапазон */
@media (orientation: portrait) { }        /* Вертикальная ориентация */
@media print { }                          /* При печати */

Брейкпоинты — стандартные точки перелома

/* Mobile first (рекомендуемый подход) */
/* Базовые стили — для мобильных */
.container { width: 100%; padding: 0 16px; }

@media (min-width: 768px) {   /* Планшет и выше */
  .container { max-width: 768px; margin: 0 auto; }
}

@media (min-width: 1024px) {  /* Десктоп и выше */
  .container { max-width: 1200px; }
}

@media (min-width: 1280px) {  /* Широкий десктоп */
  .container { max-width: 1440px; }
}

Популярные брейкпоинты (Tailwind CSS):

  • 640px — sm (большой телефон)
  • 768px — md (планшет)
  • 1024px — lg (маленький десктоп)
  • 1280px — xl (десктоп)
  • 1536px — 2xl (широкий экран)
  • Desktop-first vs Mobile-first

    Desktop-first (старый подход)

    /* Начинаем с десктопа, упрощаем для мобильных */
    .grid { display: grid; grid-template-columns: 1fr 1fr 1fr; }
    
    @media (max-width: 768px) {
      .grid { grid-template-columns: 1fr; }
    }

    Mobile-first (рекомендуется)

    /* Начинаем с мобильного, добавляем для больших экранов */
    .grid { grid-template-columns: 1fr; }  /* Мобильный */
    
    @media (min-width: 768px) {
      .grid { grid-template-columns: 1fr 1fr; }  /* Планшет */
    }
    
    @media (min-width: 1024px) {
      .grid { grid-template-columns: 1fr 1fr 1fr; }  /* Десктоп */
    }

    Mobile-first лучше: мобильные получают минимальный CSS (быстрее загрузка), сложность добавляется постепенно.

    Viewport meta tag

    Без этого тега браузер на мобильном притворяется, что экран 980px:

    <meta name="viewport" content="width=device-width, initial-scale=1">

    Это обязательная строка в <head> любого современного сайта. Без неё медиа-запросы работать не будут корректно.

    JavaScript — window.matchMedia

    const isMobile = window.matchMedia('(max-width: 768px)').matches
    
    window.matchMedia('(min-width: 1024px)').addEventListener('change', (e) => {
      console.log('Десктоп:', e.matches)
    })

    Типичные ошибки

    Ошибка 1: Использовать px вместо rem в медиа-запросах

    /* px не учитывают системный масштаб текста пользователя */
    @media (max-width: 768px) { }   /* Стандартно, но не идеально */
    @media (max-width: 48em) { }    /* 768px / 16 = 48em — лучше для доступности */

    Ошибка 2: Слишком много брейкпоинтов

    /* Плохо: много специфичных значений */
    @media (max-width: 1200px) { }
    @media (max-width: 1150px) { }
    @media (max-width: 900px) { }
    @media (max-width: 850px) { }
    
    /* Хорошо: 2-4 чётких брейкпоинта */
    @media (min-width: 768px) { }
    @media (min-width: 1024px) { }

    Ошибка 3: Не проверять на реальных устройствах

    DevTools эмулируют размеры, но не все браузерные особенности. Проверяй на телефоне.

    В реальных проектах

    Tailwind CSS заменяет медиа-запросы префиксами: md:grid-cols-2 = "при ≥768px — две колонки". Bootstrap использует систему брейкпоинтов xs/sm/md/lg/xl. Под капотом — те же @media запросы, просто автоматизированные.

    Примеры

    Определение размера экрана и адаптивное поведение через JS

    // window.matchMedia — JavaScript-эквивалент медиа-запросов
    const breakpoints = {
      mobile:  window.matchMedia('(max-width: 767px)'),
      tablet:  window.matchMedia('(min-width: 768px) and (max-width: 1023px)'),
      desktop: window.matchMedia('(min-width: 1024px)'),
    }
    
    console.log('=== Текущий размер экрана ===')
    console.log('Мобильный (≤767px):', breakpoints.mobile.matches)
    console.log('Планшет (768-1023px):', breakpoints.tablet.matches)
    console.log('Десктоп (≥1024px):', breakpoints.desktop.matches)
    console.log('Ширина окна:', window.innerWidth + 'px')
    
    // Определяем текущее устройство
    function getDevice() {
      if (window.innerWidth < 768) return 'mobile'
      if (window.innerWidth < 1024) return 'tablet'
      return 'desktop'
    }
    console.log('Устройство:', getDevice())
    
    // Адаптивная конфигурация
    const config = {
      mobile:  { columns: 1, cardWidth: '100%', fontSize: '14px' },
      tablet:  { columns: 2, cardWidth: '48%',  fontSize: '15px' },
      desktop: { columns: 3, cardWidth: '31%',  fontSize: '16px' },
    }
    
    const device = getDevice()
    const current = config[device]
    console.log('Колонок для этого экрана:', current.columns)
    console.log('Ширина карточки:', current.cardWidth)
    
    // Создаём адаптивную сетку
    const grid = document.createElement('div')
    grid.style.display = 'grid'
    grid.style.gridTemplateColumns = `repeat(${current.columns}, 1fr)`
    grid.style.gap = '12px'
    grid.style.padding = '16px'
    document.body.appendChild(grid)
    
    for (let i = 1; i <= 6; i++) {
      const card = document.createElement('div')
      card.style.backgroundColor = `hsl(${i * 40}, 60%, 85%)`
      card.style.padding = '16px'
      card.style.borderRadius = '8px'
      card.style.fontSize = current.fontSize
      card.textContent = 'Карточка ' + i
      grid.appendChild(card)
    }
    
    console.log('Сетка создана с', current.columns, 'колонками')
    
    // Ориентация устройства
    const isPortrait = window.matchMedia('(orientation: portrait)').matches
    console.log('Портретная ориентация:', isPortrait)
    
    // window.screen — физические размеры экрана
    console.log('Физический экран:', window.screen.width + 'x' + window.screen.height)

    Медиа-запросы и адаптивность

    Больше половины трафика в интернете приходит с мобильных. Сайт, который не адаптируется под экраны — это сайт, который теряет половину пользователей. Медиа-запросы позволяют применять разные стили в зависимости от ширины экрана, ориентации устройства и других характеристик.

    Что такое медиа-запрос

    @media — это условный оператор в CSS. Если условие выполняется, стили внутри применяются.

    @media (max-width: 768px) {
      /* Эти стили активны только на экранах шириной ≤ 768px */
      .nav { display: none; }
      .hamburger { display: block; }
    }

    Синтаксис

    @media тип_медиа и (условие) {
      /* CSS-правила */
    }
    
    /* Примеры */
    @media screen and (max-width: 768px) { }   /* Только экраны, ≤768px */
    @media (min-width: 1024px) { }             /* ≥1024px (тип screen подразумевается) */
    @media (min-width: 768px) and (max-width: 1023px) { }  /* Диапазон */
    @media (orientation: portrait) { }        /* Вертикальная ориентация */
    @media print { }                          /* При печати */

    Брейкпоинты — стандартные точки перелома

    /* Mobile first (рекомендуемый подход) */
    /* Базовые стили — для мобильных */
    .container { width: 100%; padding: 0 16px; }
    
    @media (min-width: 768px) {   /* Планшет и выше */
      .container { max-width: 768px; margin: 0 auto; }
    }
    
    @media (min-width: 1024px) {  /* Десктоп и выше */
      .container { max-width: 1200px; }
    }
    
    @media (min-width: 1280px) {  /* Широкий десктоп */
      .container { max-width: 1440px; }
    }

    Популярные брейкпоинты (Tailwind CSS):

  • 640px — sm (большой телефон)
  • 768px — md (планшет)
  • 1024px — lg (маленький десктоп)
  • 1280px — xl (десктоп)
  • 1536px — 2xl (широкий экран)
  • Desktop-first vs Mobile-first

    Desktop-first (старый подход)

    /* Начинаем с десктопа, упрощаем для мобильных */
    .grid { display: grid; grid-template-columns: 1fr 1fr 1fr; }
    
    @media (max-width: 768px) {
      .grid { grid-template-columns: 1fr; }
    }

    Mobile-first (рекомендуется)

    /* Начинаем с мобильного, добавляем для больших экранов */
    .grid { grid-template-columns: 1fr; }  /* Мобильный */
    
    @media (min-width: 768px) {
      .grid { grid-template-columns: 1fr 1fr; }  /* Планшет */
    }
    
    @media (min-width: 1024px) {
      .grid { grid-template-columns: 1fr 1fr 1fr; }  /* Десктоп */
    }

    Mobile-first лучше: мобильные получают минимальный CSS (быстрее загрузка), сложность добавляется постепенно.

    Viewport meta tag

    Без этого тега браузер на мобильном притворяется, что экран 980px:

    <meta name="viewport" content="width=device-width, initial-scale=1">

    Это обязательная строка в <head> любого современного сайта. Без неё медиа-запросы работать не будут корректно.

    JavaScript — window.matchMedia

    const isMobile = window.matchMedia('(max-width: 768px)').matches
    
    window.matchMedia('(min-width: 1024px)').addEventListener('change', (e) => {
      console.log('Десктоп:', e.matches)
    })

    Типичные ошибки

    Ошибка 1: Использовать px вместо rem в медиа-запросах

    /* px не учитывают системный масштаб текста пользователя */
    @media (max-width: 768px) { }   /* Стандартно, но не идеально */
    @media (max-width: 48em) { }    /* 768px / 16 = 48em — лучше для доступности */

    Ошибка 2: Слишком много брейкпоинтов

    /* Плохо: много специфичных значений */
    @media (max-width: 1200px) { }
    @media (max-width: 1150px) { }
    @media (max-width: 900px) { }
    @media (max-width: 850px) { }
    
    /* Хорошо: 2-4 чётких брейкпоинта */
    @media (min-width: 768px) { }
    @media (min-width: 1024px) { }

    Ошибка 3: Не проверять на реальных устройствах

    DevTools эмулируют размеры, но не все браузерные особенности. Проверяй на телефоне.

    В реальных проектах

    Tailwind CSS заменяет медиа-запросы префиксами: md:grid-cols-2 = "при ≥768px — две колонки". Bootstrap использует систему брейкпоинтов xs/sm/md/lg/xl. Под капотом — те же @media запросы, просто автоматизированные.

    Примеры

    Определение размера экрана и адаптивное поведение через JS

    // window.matchMedia — JavaScript-эквивалент медиа-запросов
    const breakpoints = {
      mobile:  window.matchMedia('(max-width: 767px)'),
      tablet:  window.matchMedia('(min-width: 768px) and (max-width: 1023px)'),
      desktop: window.matchMedia('(min-width: 1024px)'),
    }
    
    console.log('=== Текущий размер экрана ===')
    console.log('Мобильный (≤767px):', breakpoints.mobile.matches)
    console.log('Планшет (768-1023px):', breakpoints.tablet.matches)
    console.log('Десктоп (≥1024px):', breakpoints.desktop.matches)
    console.log('Ширина окна:', window.innerWidth + 'px')
    
    // Определяем текущее устройство
    function getDevice() {
      if (window.innerWidth < 768) return 'mobile'
      if (window.innerWidth < 1024) return 'tablet'
      return 'desktop'
    }
    console.log('Устройство:', getDevice())
    
    // Адаптивная конфигурация
    const config = {
      mobile:  { columns: 1, cardWidth: '100%', fontSize: '14px' },
      tablet:  { columns: 2, cardWidth: '48%',  fontSize: '15px' },
      desktop: { columns: 3, cardWidth: '31%',  fontSize: '16px' },
    }
    
    const device = getDevice()
    const current = config[device]
    console.log('Колонок для этого экрана:', current.columns)
    console.log('Ширина карточки:', current.cardWidth)
    
    // Создаём адаптивную сетку
    const grid = document.createElement('div')
    grid.style.display = 'grid'
    grid.style.gridTemplateColumns = `repeat(${current.columns}, 1fr)`
    grid.style.gap = '12px'
    grid.style.padding = '16px'
    document.body.appendChild(grid)
    
    for (let i = 1; i <= 6; i++) {
      const card = document.createElement('div')
      card.style.backgroundColor = `hsl(${i * 40}, 60%, 85%)`
      card.style.padding = '16px'
      card.style.borderRadius = '8px'
      card.style.fontSize = current.fontSize
      card.textContent = 'Карточка ' + i
      grid.appendChild(card)
    }
    
    console.log('Сетка создана с', current.columns, 'колонками')
    
    // Ориентация устройства
    const isPortrait = window.matchMedia('(orientation: portrait)').matches
    console.log('Портретная ориентация:', isPortrait)
    
    // window.screen — физические размеры экрана
    console.log('Физический экран:', window.screen.width + 'x' + window.screen.height)

    Задание

    Создай адаптивную страницу с тремя карточками. На мобильных устройствах (до 767px) карточки должны стоять в один столбец, на планшетах (768px–1023px) — в два столбца, на десктопе (от 1024px) — в три столбца. Используй `@media` запросы с подходом mobile-first.

    Подсказка

    Mobile-first: базовые стили для мобильного (`1fr`). Затем `@media (min-width: 768px)` — две колонки (`1fr 1fr`). Затем `@media (min-width: 1024px)` — три колонки (`repeat(3, 1fr)`). Не забудь `<meta name="viewport">`.

    Загружаем среду выполнения...
    Загружаем AI-помощника...