본문 바로가기

FrontEnd

[CSS] absolute positioning을 grid로 대체하기

반응형

원문 : https://ishadeed.com/article/less-absolute-positioning-modern-css/

 

Less Absolute Positioning With Modern CSS - Ahmad Shadeed

How to use use position:absolute less by leveraging modern CSS

ishadeed.com

TL;DR

  • grid
  • display : contents
  • aspect-ratio

position : absolute를 활용한 카드 오버레이

구현 방식엔 전혀 문제가 없음

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 그리드는 디폴트로 콘텐츠를 기반으로 자동으로 행을 생성함.
카드에는 두 가지 메인 요소가 있으므로 두 행의 콘텐츠가 있음

CSS 그리드는 디폴트로 콘텐츠를 기반으로 자동으로 행을 생성함.

콘텐츠를 이미지와 겹치게 하려면 둘 다 첫 번째 그리드 영역에 배치해야 함.
.card__thumb,
.card__content {
    grid-column: 1/2;
    grid-row: 1/2;
}
grid-area shorthand property를 사용할 수도 있음
.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 */
}

Demo


Hero 영역

콘텐츠와 이미지가 겹치는 hero 영역에도 해당 아이디어를 적용할 수 있음

hero 영역

그리드 없이는 보통 3중첩 레이어로 해당 hero를 구성함

  • Image
  • Gradient overlay
  • Content

 

3중첩 레이어로 해당 hero를 구성함

이미지가 단순히 장식용인 경우 배경 이미지를 사용함.

그렇지 않은 경우 <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;
}
  1. 먼저 hero 요소에 display: grid를 추가함.
  2. 그 다음 모든 직계 자식 항목에 grid-area: 1/-1을 적용하는 카드 컴포넌트와 동일한 개념을 적용함

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;
}

Demo

  1. 그리드 또는 플렉스 아이템에 Z-인덱스를 사용 가능
    • position:relative를 추가할 필요가 없음
  2. .hero__content는 그리드 아이템이므로 margin: auto를 사용하면 수평 및 수직으로 중앙에 배치됨.
  3. 이미지를 다룰 때 object-fit: cover를 포함하는 것을 잊지 말 것
  4. 이미지가 너무 커질 것을 대비해(just in case) min-height:0 설정
    • height : 100%을 준수하도록 함
    • 거대한 이미지가 hero section보다 이미지를 더 크게 하지 핞도록 함
    • 참고 : minimum content size in CSS grid

CSS display : contents

아래와 같은 디자인을 생각해보자

좌우 정렬 hero

<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;
    }
}

Demo 


카드 컴포넌트 아이템 재배치

카드 컴포넌트 아이템 재배치

카드 상단에 제목을 배치하는 변종 카드가 있음
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;
}
이렇게 하면 모든 항목의 순서를 order를 이용해 제어할 수 있게 됨.

display : contents와 order 속성을 이용해 항목 순서를 쉽게 제어

부모 컨텐츠에 패딩 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가 더 나은 경우

프로파일 카드

카드 표지(커버)와 겹치는 콘텐츠(아바타, 이름 및 링크)가 존재함.
이를 구현하기 위한 두 가지 옵션이 있음
  • 카드 커버의 절대 배치
  • 콘텐츠에 음수 마진

카드 커버의 절대 배치

카드 커버의 절대 배치

사각형을 절대적으로 배치한 다음 패딩: 1rem을 카드 콘텐츠에 추가할 수 있음.
.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;
}
결과적으로 position: absolute를 사용하는 것이 추가 CSS를 작성하지 않아도 되기에, 더 나은 솔루션임

더 읽어보기

 

반응형