Меньше абсолютного позиционирования с современным CSS
В этой статье я рассмотрю несколько вариантов использования, в которых абсолютное позиционирование не требуется.
Вступление
Содержание статьи:
Если мы вернемся на 5 лет назад, CSS flexbox был все еще новым и не мог использовать надлежащих отступов. CSS-grid тогда даже не поддерживалась. В конце концов, мы использовали позиционирование CSS для достижения желаемого макета. Однако в наши дни некоторые из этих вариантов использования можно решить с помощью CSS flexbox или grid.
На днях мы работали над фронтендом целевой страницы, и мой партнер спросил меня о проблеме с макетом, с которой он столкнулся. Проблема возникла из-за position:absolute, я попытался помочь, и мы исправили эту проблему, даже не используя позиционирование CSS.
Случаи использования с примерами
Наложение в карточке
Когда у нас есть карточка, содержащая текст поверх изображения, мы часто используем position:absolute для размещения содержимого поверх изображения. В CSS-сетке это больше не требуется.
Практический курс по верстке адаптивного сайта с нуля!
Изучите курс и узнайте, как верстать современные сайты на HTML5 и CSS3
Это типичная карточка с текстом поверх изображения. Вот HTML:
<article class=”card”> <div class=”card__thumb”> <img src=”assets/mini-cheesecake.jpg” alt=””> </div> <div class=”card__content”> <h2><a href=”#”>Title</a></h2> <p>Subtitle</p> </div> </article>
123456789 | <article class=”card”> <div class=”card__thumb”> <img src=”assets/mini-cheesecake.jpg” alt=””> </div> <div class=”card__content”> <h2><a href=”#”>Title</a></h2> <p>Subtitle</p> </div></article> |
Чтобы разместить контент поверх изображения, нам нужно позиционировать .card__content абсолютно.
CSS .card { position: relative; } .card__content { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: linear-gradient(to top, #000, rgba(0, 0, 0, 0) bottom/100% 60% no-repeat; padding: 1rem; }
12345678910111213 | .card { position: relative;} .card__content { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background: linear-gradient(to top, #000, rgba(0, 0, 0, 0) bottom/100% 60% no-repeat; padding: 1rem;} |
В приведенном выше примере нет ничего плохого, но почему бы не использовать более простое решение? Давайте исследуем это.
Первым делом нужно добавить display:grid в компонент карты. Нам не нужно устанавливать какие-либо столбцы или строки.
CSS .card { position: relative; display: grid; } .card__content { display: flex; flex-direction: column; justify-content: flex-end; }
12345678910 | .card { position: relative; display: grid;} .card__content { display: flex; flex-direction: column; justify-content: flex-end;} |
По умолчанию сетка CSS автоматически создает строки на основе содержимого. В карте у нас есть два основных элемента, поэтому у нас есть две строки контента. Чтобы перекрыть контент с изображением, нам нужно разместить их оба в первой области сетки.
CSS .card__thumb, .card__content { grid-column: 1/2; grid-row: 1/2; }
12345 | .card__thumb,.card__content { grid-column: 1/2; grid-row: 1/2;} |
Более того, мы можем использовать сокращение grid-area:
CSS .card__thumb, .card__content { grid-area: 1/2; }
1234 | .card__thumb,.card__content { grid-area: 1/2;} |
Наконец, мы также можем использовать grid-area: 1/-1. -1 представляет собой последний столбец и строку в сетке.
Card Tag
Основываясь на предыдущем примере, мы хотим расширить его и включить тег в левую верхнюю часть карты. Для этого нам нужно использовать ту же технику grid-area: 1/-1, но мы не хотим, чтобы тег заполнял всю карту, верно?
Чтобы исправить это, нам нужно указать тегу, чтобы он выровнялся по началу своего контейнера.
CSS .card__tag { align-self: start; justify-self: start; /* Other styles */ }
12345 | .card__tag { align-self: start; justify-self: start; /* Other styles */} |
Познакомьтесь с тегом, который расположен над его родительским элементом, без использования position:absolute. Демо.
Herо-раздел
Еще один идеальный вариант использования — это hero-раздел с содержимым, перекрывающим изображение.
Это похоже на пример карты, но с другим стилем для самого содержимого. Прежде чем погрузиться в современный подход, давайте на минутку задумаемся о том, как это сделать с помощью абсолютного позиционирования. У нас есть три слоя: изображение, градиент наложения и содержание.
Есть разные способы реализовать это. Если изображение носит чисто декоративный характер, можно использовать background-image. В противном случае мы можем использовать элемент img.
CSS .hero { position: relative; min-height: 500px; } .hero__thumb { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; } /* The overlay */ .hero:after { content: “”; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #000; opacity: 0.5; } .hero__content { position: absolute; left: 50%; top: 50%; z-index: 1; transform: translate(-50%, -50%); text-align: center; }
12345678910111213141516171819202122232425262728293031323334 | .hero { position: relative; min-height: 500px;} .hero__thumb { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover;} /* The overlay */.hero:after { content: “”; position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #000; opacity: 0.5;} .hero__content { position: absolute; left: 50%; top: 50%; z-index: 1; transform: translate(-50%, -50%); text-align: center;} |
Вот как мы можем реализовать hero-раздел, используя абсолютное позиционирование. Однако мы можем добиться большего. Давайте исследуем современный подход.
Во-первых, нам нужно добавить display:grid к hero-элементу. После этого мы применим ту же концепцию, что и в компоненте карты grid-area:1/-1, к каждому прямому дочернему элементу.
К сожалению, нам нужно будет использовать фиксированную высоту для hero-секции, чтобы сработало правило .hero__thumb. (Дочерний элемент с height:100% требует, чтобы его родительский элемент имел явную фиксацию height, а не min-height).
CSS .hero { display: grid; height: 500px; } .hero__content { z-index: 1; /* [1] */ grid-area: 1/-1; display: flex; flex-direction: column; margin: auto; /* [2] */ text-align: center; } .hero__thumb { grid-area: 1/-1; object-fit: cover; /* [3] */ width: 100%; height: 100%; min-height: 0; /* [4] */ } .hero:after { content: “”; background-color: #000; opacity: 0.5; grid-area: 1/-1; }
12345678910111213141516171819202122232425262728 | .hero { display: grid; height: 500px;} .hero__content { z-index: 1; /* [1] */ grid-area: 1/-1; display: flex; flex-direction: column; margin: auto; /* [2] */ text-align: center;} .hero__thumb { grid-area: 1/-1; object-fit: cover; /* [3] */ width: 100%; height: 100%; min-height: 0; /* [4] */} .hero:after { content: “”; background-color: #000; opacity: 0.5; grid-area: 1/-1;} |
Я хочу пройтись по пронумерованным строкам кода, поскольку они важны:
Мы можем использовать z-index сетки или flex элемента. Вообще не нужно добавлять position:relative.
Поскольку .hero__content это элемент сетки, использование margin:auto будет центрировать его по горизонтали и вертикали.
Не забывайте включать object-fit:cover при работе с изображениями.
Я использовал для hero-изображения min-height:0 в том случае, если использовалось большое изображение. Это сделано для того, чтобы заставить CSS-сетку учитывать height:100% и избегать увеличения размера изображения, больше чем hero-раздел, в случае, если автор использовал огромное изображение. Подробнее об этом вы можете прочитать в моей статье о минимальном размере содержимого в CSS-сетке.
Обратите внимание, как мы пришли к гораздо более чистому решению. Демо.
CSS display:contents
Я думаю, что это первый практический пример использования display:contents, который был для меня действительно полезный. Рассмотрим следующий пример, где у нас есть контент и изображение.
<div class=”hero”> <div class=”hero__content”> <h2><!– Title –></h2> <p><!– Desc –></p> <a href=”#”>Order now</a> </div> <img src=”recipe.jpg” alt=””> </div>
12345678 | <div class=”hero”> <div class=”hero__content”> <h2><!– Title –></h2> <p><!– Desc –></p> <a href=”#”>Order now</a> </div> <img src=”recipe.jpg” alt=””></div> |
До сих пор ничего странного. На мобильных устройствах мы хотим добиться следующего макета.
Обратите внимание, что изображение теперь вставлено между заголовком и описанием. Как можно решить эту проблему? Поначалу может показаться, что разметка HTML должна быть изменена именно так.
<div class=”hero”> <div class=”hero__content”> <h2><!– Title –></h2> <img src=”recipe.jpg” alt=””> <p><!– Desc –></p> <a href=”#”>Order now</a> </div> </div>
12345678 | <div class=”hero”> <div class=”hero__content”> <h2><!– Title –></h2> <img src=”recipe.jpg” alt=””> <p><!– Desc –></p> <a href=”#”>Order now</a> </div></div> |
На мобильном телефоне все может работать как положено. На десктопе нам нужно расположить img с правой стороны. И хотя это работает, но есть более простое решение display:contents. Вернемся к исходной разметке.
<div class=”hero”> <div class=”hero__content”> <h2><!– Title –></h2> <p><!– Desc –></p> <a href=”#”>Order now</a> </div> <img src=”recipe.jpg” alt=””> </div>
12345678 | <div class=”hero”> <div class=”hero__content”> <h2><!– Title –></h2> <p><!– Desc –></p> <a href=”#”>Order now</a> </div> <img src=”recipe.jpg” alt=””></div> |
Обратите внимание, что содержимое завернуто внутрь .hero__content. Как мы можем сказать браузеру, что мы хотим, чтобы <h2>, <p> и <a> были наследниками того же родительского элемента, что и img? Что ж, с display:contents я могу это сделать:
Практический курс по верстке адаптивного сайта с нуля!
Изучите курс и узнайте, как верстать современные сайты на HTML5 и CSS3
CSS .hero__content { display: contents; }
123 | .hero__content { display: contents;} |
Браузер проанализирует разметку следующим образом.
<div class=”hero”> <h2><!– Title –></h2> <p><!– Desc –></p> <a href=”#”>Order now</a> <img src=”recipe.jpg” alt=””> </div>
123456 | <div class=”hero”> <h2><!– Title –></h2> <p><!– Desc –></p> <a href=”#”>Order now</a> <img src=”recipe.jpg” alt=””></div> |
Все, что нам нужно сделать в CSS, это следующее.
CSS .hero { display: flex; flex-direction: column; } .hero__content { display: contents; } .hero h2, .hero img { order: -1; }
12345678910111213 | .hero { display: flex; flex-direction: column;} .hero__content { display: contents;} .hero h2,.hero img { order: -1;} |
При правильном использовании display:contents — это мощный метод для достижения того, что было невозможно несколько лет назад. Конечно, мы также хотим заняться стилем для десктопа.
CSS @media (min-width: 750px) { .hero { flex-direction: row; } .hero__content { display: initial; } .hero h2, .hero img { order: initial; } }
1234567891011121314 | @media (min-width: 750px) { .hero { flex-direction: row; } .hero__content { display: initial; } .hero h2, .hero img { order: initial; }} |
Демо
Изменение порядка элементов компонентов карты
В этом примере у нас есть вариант карты, в которой заголовок помещается вверху карты. Давайте посмотрим на разметку HTML.
<article class=”card”> <img src=”thumb.jpg” alt=””> <div class=”card__content”> <h3>Title</h3> <p>Description</p> <p>Actions</p> </div> </article>
12345678 | <article class=”card”> <img src=”thumb.jpg” alt=””> <div class=”card__content”> <h3>Title</h3> <p>Description</p> <p>Actions</p> </div></article> |
Обратите внимание, что у нас есть два прямых дочерних элемента: ползунок и содержимое. Как с такой разметкой мы можем расположить заголовок h3 вверху? Мы можем сделать это с помощью абсолютного позиционирования.
CSS .card { position: relative; padding-top: 3rem; /* Accommodate for the title space */ } .card h3 { position: absolute; left: 1rem; top: 1rem; }
1234567891011 | .card { position: relative; padding-top: 3rem; /* Accommodate for the title space */} .card h3 { position: absolute; left: 1rem; top: 1rem;} |
Однако это решение может не сработать, если заголовок слишком длинный.
Это происходит потому, что заголовок выходит за рамки обычного потока, поэтому браузер фактически не заботится о том, насколько он короткий или длинный. Мы можем найти гораздо лучшее решение используя display:contents:
CSS .card { display: flex; flex-direction: column; padding: 1rem; } .card__content { display: contents; } .card h3 { order: -1; }
12345678910111213 | .card { display: flex; flex-direction: column; padding: 1rem;} .card__content { display: contents;} .card h3 { order: -1;} |
Это позволит нам контролировать все дочерние элементы и иметь возможность записывать их по мере необходимости с помощью свойства order.
У нас все еще есть небольшая проблема, так как мы добавили padding:1 rem родительскому элементу. Изображение должно быть приклеено к краям. Вот как мы можем это исправить:
CSS .card img { width: calc(100% + 2rem); margin-left: -1rem; }
1234 | .card img { width: calc(100% + 2rem); margin-left: -1rem;} |
Центрирование
Когда дело доходит до центрирования, вы увидите множество шуток о том, как это сложно. В наши дни центрировать элемент намного проще, чем когда-либо.
Больше не нужно использовать преобразования position:absolute. Например, мы можем использовать margin:auto для центрирования гибкого элемента как по горизонтали, так и по вертикали.
CSS .card { display: flex; } .card__image { margin: auto; }
1234567 | .card { display: flex;} .card__image { margin: auto;} |
Соотношение сторон изображения
Новое свойство CSS aspect-ratio теперь поддерживается во всех основных браузерах. Раньше мы использовали отступы, чтобы соблюдать определенное соотношение сторон.
CSS .card__thumb { position: relative; padding-top: 75%; } .card__thumb img { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; }
12345678910111213 | .card__thumb { position: relative; padding-top: 75%;} .card__thumb img { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover;} |
С новым aspect-ratio это намного проще.
CSS /* */ .card__thumb { position: relative; aspect-ratio: 4/3; }
12345 | /* */.card__thumb { position: relative; aspect-ratio: 4/3;} |
Иногда лучше использовать рosition:аbsolute
В этом примере у нас есть контент (аватар, имя и ссылка), перекрывающий обложку карты. У нас есть два варианта реализации этого:
Использовать position:absolute для обложки карты
Использовать отрицательное поле для содержимого
Давайте рассмотрим решения и посмотрим, какое из них больше подходит для этого варианта использования.
Использование абсолютного позиционирования
С помощью этого решения мы можем позиционировать прямоугольник абсолютно, а затем добавить padding:1rem к содержимому карты.
CSS .card__cover { position: absolute; left: 0; top: 0; width: 100%; height: 50px; } .card__content { padding: 1rem; }
1234567891011 | .card__cover { position: absolute; left: 0; top: 0; width: 100%; height: 50px;} .card__content { padding: 1rem;} |
Таким образом, когда покрытие карты убирается, нам не нужно редактировать или изменять CSS.
Использование отрицательных полей
В этом решении карта не будет позиционироваться абсолютно. Вместо этого контент будет иметь отрицательное поле сверху.
CSS .card__content { padding: 1rem; margin-top: -1rem; }
1234 | .card__content { padding: 1rem; margin-top: -1rem;} |
Хотя это решение работает при наличии покрытия карты, оно может вызвать проблемы, когда у нас есть вариант карты без покрытия.
Обратите внимание, как аватар находится за пределами своего родителя (карты). Чтобы исправить это, нам нужно изменить CSS и удалить отрицательное поле.
CSS .card–no-cover .card__content { margin-top: 0; }
123 | .card–no-cover .card__content { margin-top: 0;} |
В результате — использование position:absolute оказалось лучшим решением, так как избавит нас от написания дополнительных CSS. Надеюсь, вам понравилась статья. Спасибо за чтение!
Автор: Ahmad Shadeed
Источник: webformyself.com