[CSS] absolute positioning을 grid로 대체하기
원문 : https://ishadeed.com/article/less-absolute-positioning-modern-css/
TL;DR
- grid
- display : contents
- aspect-ratio
position : absolute를 활용한 카드 오버레이
구현 방식엔 전혀 문제가 없음
<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>
<style>
.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;
}
</style>
display: grid를 활용한 카드 오버레이
display : grid를 사용해 해당 레이아웃을 구현해 보겠음.
먼저 아래와 같이 CSS를 적용함
<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>
<style>
.card {
position: relative;
display: grid;
}
.card__content {
display: flex;
flex-direction: column;
justify-content: flex-end;
}
</style>
CSS 그리드는 디폴트로 콘텐츠를 기반으로 자동으로 행을 생성함.
카드에는 두 가지 메인 요소가 있으므로 두 행의 콘텐츠가 있음
.card__thumb,
.card__content {
grid-column: 1/2;
grid-row: 1/2;
}
.card__thumb,
.card__content {
grid-area: 1/2;
}
grid-area: 1/-1을 사용할 수도 있음.
-1은 그리드의 마지막 열과 행을 나타내므로 항상 한 행과 한 열을 모두 차지하게 됨
카드 태그 추가하기
카드 상단에 태그를 포함하고 싶음.
이것도 그리드로 가능할까?
해당 카드 요소에만 정렬(align-self, justify-self)을 추가하면 됨
.card__tag {
align-self: start;
justify-self: start;
/* Other styles */
}
Hero 영역
콘텐츠와 이미지가 겹치는 hero 영역에도 해당 아이디어를 적용할 수 있음
그리드 없이는 보통 3중첩 레이어로 해당 hero를 구성함
- Image
- Gradient overlay
- Content
이미지가 단순히 장식용인 경우 배경 이미지를 사용함.
그렇지 않은 경우 <img/>태그를 사용하여 이를 구현할 수 있음.
.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를 추가함.
- 그 다음 모든 직계 자식 항목에 grid-area: 1/-1을 적용하는 카드 컴포넌트와 동일한 개념을 적용함
(아쉽지만) .hero__thumb이 실제로 작동하려면 hero 섹션에 고정 높이를 사용해야 함.
- 높이가 100%인 하위 항목은 최소 높이가 아니라 명시적으로 고정된 높이를 갖는 상위 항목이 필요하기 때문.
.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-인덱스를 사용 가능
- position:relative를 추가할 필요가 없음
- .hero__content는 그리드 아이템이므로 margin: auto를 사용하면 수평 및 수직으로 중앙에 배치됨.
- 이미지를 다룰 때 object-fit: cover를 포함하는 것을 잊지 말 것
- 이미지가 너무 커질 것을 대비해(just in case) min-height:0 설정
- height : 100%을 준수하도록 함
- 거대한 이미지가 hero section보다 이미지를 더 크게 하지 핞도록 함
- 참고 : minimum content size in CSS grid
CSS 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>
모바일에서는 다음과 같은 레이아웃을 구현하고 싶음
헤드라인과 설명 사이에 이미지가 삽입됨
마크업을 변경해야 할까?
<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>
모바일에서는 잘 동작하겠으나, 데스크톱에서는 이미지를 절대 위치로 오른쪽에 배치해야할 것임.
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>
display: contents를 사용하면 브라우저에 h2, p , a가 img와 형제가 될 수 있다고 알릴 수 있음
.hero__content {
display: contents;
}
이제 hero__content 클래스는 레이아웃에 영향을 주지 않음.
브라우저는 이제 마크업을 아래와 같이 해석함
<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;
}
잘만 사용하면 display: contents는 몇 년 전에는 불가능했던 일을 달성할 수 있는 강력한 기술임
데스크탑일 때는 아래와 같은 스타일을 적용하면 됨
@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>
썸네일 컴포넌트와 컨텐츠만 직계 자손임
h3을 어떻게 최상단으로 올릴까?
absoulte 포지셔닝을 사용하면 간단함
.card {
position: relative;
padding-top: 3rem;
/* Accommodate for the title space */
}
.card h3 {
position: absolute;
left: 1rem;
top: 1rem;
}
하지만 이 방법은 텍스트가 길어지면 문제가 발생함.
(한 줄로 제한하는 방법이 있긴 함)
제목이 정상적인 흐름을 벗어나
브라우저가 컨텐츠가 실제로 얼마나 짧거나 긴지 신경 쓰지 않기 때문에 이런 일이 발생함.
display: contents를 사용하면 더 잘할 수 있음
.card {
display: flex;
flex-direction: column;
padding: 1rem;
}
.card__content {
display: contents;
}
.card h3 {
order: -1;
}
부모 컨텐츠에 패딩 1rem이 적용되어 문제가 발생함.
아래 CSS로 문제를 해결할 수 있음
.card img {
width: calc(100% + 2rem);
margin-left: -1rem;
}
카드 중앙 정렬
이전에는 position:absolute, top: 50%, Y축 트랜스폼 기술을 사용했음
.card {
display: flex;
}
.card__image {
margin: auto;
}
자세한 내용은 complete guide 참조
이미지 종횡비(Aspect Ratio)
예전에는 이미지 종횡비를 만들기 위해 패딩과 절대 위치를 사용했음
.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(더 자세히 알아보기)를 활용하자.
/* */
.card__thumb {
position: relative;
aspect-ratio: 4/3;
}
position : absolute가 더 나은 경우
- 카드 커버의 절대 배치
- 콘텐츠에 음수 마진
카드 커버의 절대 배치
.card__cover {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 50px;
}
.card__content {
padding: 1rem;
}
이렇게 하면 카드 커버를 제거할 때 CSS를 수정하거나 변경할 필요가 없음.
음수 마진 사용
이 솔루션에서는 카드가 절대적으로 배치되지 않음
대신 카드 콘텐츠 상단에 음수 마진을 포함하도록 해야 함
.card__content {
padding: 1rem;
margin-top: -1rem;
}
이를 수정하려면 CSS를 변경하여 음수 마진을 제거해야 함
.card--no-cover .card__content {
margin-top: 0;
}
더 읽어보기
- Ryan Mulligan published a great article on positioning overlay content with CSS grid.
- Stephanie Eckles wrote a wonderful piece on using CSS grid to build hero sections.