Несколько месяцев назад я добавил себе в закладки твит, это был настоящий трюк CSS, то есть именно то, что мы любим.

Итак, вопрос: как сделать контейнер полноэкранной ширины внутри родительского элемента ограниченной ширины?

Введение

Изображение, занимающее 100% родительского контейнера

Мы хотим растянуть это изображение ровно на ширину окна браузера.

Вот минимально необходимая для этого разметка:

<!-- parent -->
<main>

  <p>Stuff.</p>

  <!-- container we want to be full width -->
  <figure class="full-width">
    <!-- could be whatever content -->
    <img src="dog.jpg" alt="">
  </figure>

</main>

Размышления

Если бы мы могли использовать абсолютное позиционирование, мы бы задали контейнеру left: 0; и width: 100%;, но мы не можем этого сделать, так как хотим, чтобы контейнер остался в потоке.

Можем ли мы применить отрицательные внешние отступы с каждой стороны, тем самым раздвинув контейнер наружу? В некоторых обстоятельствах можем!

Ширина родительского элемента задана в процентах

Предположим, что ширина родительского элемента 60% и он центрирован. Это значит, что у нас есть по 20% ширины с каждой стороны. Но внешний отступ расчитывается исходя из ширины родительского элемента, а не всей ширины; в нашем случае 20% от ширины экрана это 33% от ширины родительского элемента.

main {
  width: 60%;
  margin: 0 auto;
  /* creates 20% margins on either side */
}
.full-width {
  /* 1/3 of 60% = the 20% margin on either side */
  margin-left: -33.33%;
  margin-right: -33.33%;
}

Ширина родительского элемента задана в пикселях

В большинстве случаев у нас нет достаточной информации о том, насколько надо расширять контейнер до полной ширины при помощи отрицательных внешних отступов.

Хотя…

Мы можем использовать ширину окна браузера в наших расчетах CSS. Расстояние, на которое нам надо расширить контейнер влево и вправо это половина ширины окна браузера плюс половина ширины родительского элемента (мы исходим из того, что родительский элемент центрирован).

Итак, ширина родительского элемента 500 пикселей:

.full-width {
  margin-left: calc(-100vw / 2 + 500px / 2);
  margin-right: calc(-100vw / 2 + 500px / 2);
}

Фиксированная ширина вроде этой немного тревожит (а что будет на экранах меньшей ширины?), все это стоит обернуть медиа-запросом, чтобы правила срабатывали только на больших экранах:

@media (min-width: 500px) {
  main {
    width: 500px;
    margin: 0 auto;
  }
  .full-width {
    margin-left: calc(-100vw / 2 + 500px / 2);
    margin-right: calc(-100vw / 2 + 500px / 2);
  }
}

Так как в нашей демонстрации используется изображение, имеет смысл добавить стили и ему, типа .full-width img { width: 100%; }, чтобы изображение могло занять весь экран.

Чтобы было еще проще, вы можете поэкспериментировать с демо, приведя расчеты к следующему виду:

@media (min-width: $max-width) {
  .full-width {
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
  }
}

Метод с трансформацией

Мы, как правило, не задействуем анимации, но в качестве решения для расширения контейнера можно использовать и свойства трансформации.

@media (min-width: 40em) {
  .full-width {
    width: 100vw;
    transform: translateX(calc((40em - 100vw)/2));
  }
}

Директива @supports()

Предыдущая идея была взята из пена Брэндона Мэтиса, он обертывал свой код директивой @supports.

/* See warning below */
@supports (width: 100vw) {
  .full-width {
    width: 100vw;
  }
  @media all and (min-width: 40rem) {
    .full-width {
       transform: translateX(calc((40rem - 100vw)/2));
    }
  }
}

Идея состоит в том, что эти правила не будут применяться, если браузер не поддерживает единицы измерения, относительные размеров экрана. Таким образом вы можете добавить запасной вариант к CSS и переписать эти правила.

Кажется неплохой идеей, но у меня в ходе тестирования только Firefox сработал правильно. Chrome может иногда некорректно применить медиа-запрос, когда этого делать не надо (см. скриншот — я написал “иногда”, так как похоже, что это исправляется после переотрисовки). Edge не применяет медиа-запрос совсем (скриншот), возможно, эти баги связаны с вложением директив CSS (@-правил).

Метод без использования calc()

Свен Вольферманн последовал идее Джона Нила, с которой нам не нужны расчеты с помощью calc():

.full-width {
  width: 100vw;
  position: relative;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
}

А вот сама идея: сдвинуть контейнер в центр браузера с помощью left: 50%;, а затем вернуть его к левому краю с помощью отрицательного внешнего отступа в -50vw.

контенер, занимающий всю ширину экрана

Красота! Таким образом вам не нужно вообще никакой информации о родительском элементе. Но запомните: также как и в случае с calc(), этот вариант также требует центрирования родительского элемента.

Вы можете подумать, а зачем задействовать right и margin-right? Действительно, вы можете обойтись без них на обычном сайте, текст которого располагается слева направо, но мы учитываем и вариант с direction: rtl;, таким образом, делая код пуленепробиваемым.

А вот форк этого метода, который мне кажется наиболее полезным:

See the Pen full viewport width image (container) inside article by prgssr (@prgssr) on CodePen.