본문 바로가기

FrontEnd

[CSS] 실전 Grid Layout 활용

반응형

원문 : https://ishadeed.com/article/css-grid/

 

Grid Layout Ah-ha Moment - Ahmad Shadeed

 

ishadeed.com

TL;DR

grid 레이아웃은 마크업 구조에 영향을 덜 주면서 콘텐츠의 배치를 쉽게 조절할 수 있는 방법이다.


CSS Grid란

행과 열 같은 이차원에서 컨텐츠를 제어할 수 있는 기능을 제공하는 CSS 레이아웃 모듈

 

Grid 사용하기

부모 컨테이너 요소에 display : grid를 선언

모든 자식 컴포넌트는 grid item이 됨

최소한 원하는 열 수를 지정해야 Grid의 효과를 볼 수 있음

 

grid-template-columns를 사용하여 원하는 열 수를 정의할 수 있으며 각 값은 열의 너비를 나타냄
아래에는 3개의 열이 있고 각각의 너비는 200px임

.wrapper {
  display: grid;
  grid-template-columns: 200px 200px 200px;
}

.item {
  height: 100px;
}

아래와 같이 4개의 아이템이 있게되면 자동으로 줄바꿈이 발생함

<div class="wrapper">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

항목 사이에는 간격이 없음

4개의 아이템 중 하나가 줄바꿈이 일어난 모습. 아이템 간 간격 없음


수직 간격과 수평 간격

grid-column-gap과 grid-row-gap으로 각 수직, 수평 방향 간격을 추가할 수 있음

혹은 gap : 10px; grid-gap : 10px 10px 도 가능

 

.wrapper {
  display: grid;
  grid-template-columns: 200px 200px 200px;
  grid-column-gap: 10px;
  grid-row-gap: 10px;
}

아이템 간 간격 적용

아직 열 수와 너비만 정의하였음
이렇게 하면, 아이템의 갯수와 높이를 기반으로 브라우저가 암묵적으로 행의 갯수와 높이를 결정함
 
브라우저가 자동 계산한 grid-template-rows 값

행의 높이와 갯수를 명시적으로 적어줄 수도 있음

.wrapper {
  grid-template-columns: 200px 200px 200px;
  grid-template-rows: 100px 100px; /* implicitly added by the browser */
}​

예제 1 : 뉴스 리스트 컴포넌트

5개의 뉴스 항목이 있지만, 첫 번째 항목이 특집이므로 두 개의 행을 차지하게 해보자

5개의 뉴스 항목이 있지만, 첫 번째 항목이 특집이므로 높이가 다른 항목의 두 배가 되어야 함.

5개 전부 같은 컴포넌트를 사용하더라도, grid 레이아웃을 사용해 위와 같은 효과를 볼 수 있음.

<div class="wrapper">
  <div class="item">
    <a href="#">
      <img src="img/sunrise.jpg" alt="">
      <h2>This is the title</h2>
    </a>
  </div>
  <div class="item"> ... </div>
  <div class="item"> ... </div>
  <div class="item"> ... </div>
  <div class="item"> ... </div>
</div>
.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-gap: 10px;
}

1fr은 전체에서 해당 열의 비율을 나타냄 (fraction)

1fr은 1fr/3fr => 33%을 의미

1 / 3 = 0.33 = 33%

그리드 선(Grid-line)

그리드는 그리드 컨테이너의 공간을 그리드 항목(그리드 컨테이너의 컨텐츠을 나타냄)이 배치될 수 있는 그리드 영역으로 나누는
교차하는 수평 및 수직 그리드 선 집합입니다.

...CSS Grid Spec

 

Grid에는 열과 행 모두에 그리드 선(grid line) 개념이 존재함

아래 그림에는 3개의 수직 선이 존재

Grid에는 열과 행 모두에 그리드 선(grid line) 개념이 존재함

첫번쨰 항목의 행 방향 grid-line을 나타내는 CSS는 다음과 같이 나타날 것임

.item:first-child {
  grid-row-start: 1;
  grid-row-end: 3;
}

.item:first-child {
  grid-row: 1 / 3;
}

작은 화면에서는 특집도 한 열만 차지하도록 하고, 최대 두 줄만 나타내도록 해보자

.wrapper {
  display: grid;
  grid-template-columns: 1fr; /* 1 column */
  grid-gap: 10px;
}

/* make them as 2 columns per row on viewports > 500px */
@media(min-width: 500px) {
  .wrapper {
    grid-template-columns: 1fr 1fr; /* 2 columns */
  }
}

/* make them as 2 columns per row on viewports > 900px */
@media(min-width: 900px) {
  .wrapper {
    grid-template-columns: 1fr 1fr 1fr; /* 3 columns */
  }
  
  .item:first-child {
    grid-row: 1 / 3;
  }
}

열 단위로 특집 항목 확장하기

이전 이미지에는 4개의 열이 존재함

특집 페이지가 2개의 행이 아닌 열을 차지하도록 해보자

이전 이미지에는 4개의 열이 존재함

2개의 열을 차지하는 CSS

.item:first-child {
  grid-column-start: 1;
  grid-column-end: 3;
}

속기 버전

.item:first-child {
  grid-column: 1 / 3;
}

배치 결과는 다음과 같음

2개의 열을 차지하는 특집 카드


예제 2 : 회원 가입 양식

아래와 같은 양식을 Grid로 구현해 보기

예제 2 : 회원 가입 양식

요구 사항

  • 각 input 너비 50%
  • 버튼은 100% 너비
  • 양식 필드 사이의 가로, 세로 간격

마크업

<form action="">
    <p>
        <label for="first-name">First Name:</label>
        <input type="text" id="first-name">
    </p>
    <p>
        <label for="last-name">Last Name:</label>
        <input type="text" id="last-name">
    </p>
    <p>
        <label for="email">Email Address:</label>
        <input type="email" id="email">
    </p>
    <p>
        <label for="password">Password:</label>
        <input type="password" id="password">
    </p>
    <p>
        <button>Sign up for free</button>
    </p>
</form>

각 양식 항목은 <p> 요소로 래핑되며 이를 사용하여 일부 요소를 제어함.

아래와 같이 그리드 열을 생각할 수 있음

마지막 요소(버튼)만 전체 열을 차지하도록 하면 되지 않을까?

그리드 열 배치

form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-gap: 16px;
}

/* make the button wrapper take space from line 1 to line 3 */
form p:last-child {
  grid-column: 1 / 3;
}

예제 3 : 회원 가입 양식 2

예제 3 : 회원 가입 양식

요구 사항

  • 인풋의 왼쪽에 정렬된 라벨
  • 인풋과 정렬된 버튼

이 경우는 p로 래핑할 필요가 없음.

<form action="">
    <label for="first-name">First Name:</label>
    <input type="text" id="first-name">

    <label for="last-name">Last Name:</label>
    <input type="text" id="last-name">

    <label for="email">Email Address:</label>
    <input type="email" id="email">

    <label for="password">Password:</label>
    <input type="password" id="password">

    <button>Sign up for free</button>
</form>

인풋, 버튼 열만 라벨의 2배 비율(2fr)을 적용함

인풋, 버튼 열만 라벨의 2배 비율(2fr)을 적용함

form {
  display: grid;
  grid-template-columns: 1fr 2fr;
  grid-gap: 16px;
}

button {
  grid-column: 2 / 3;
}

그리드 영역(Grid Areas)

그리드 영역은 테이블의 cell로 생각할 수 있음

각 셀은 4면으로 둘러쌓여 있고, 그리드 영역도 동일함

그리드 영역은 테이블의 cell로 생각할 수 있음

그리드 영역의 좋은 점은 각 영역에 이름을 붙이고 각 요소를 해당 영역에 배치할수 있다는 점임

grid-template-areas 속성을 사용하면 가능함

그리드 영역의 좋은 점은 각 영역에 이름을 붙이고 각 요소를 해당 영역에 배치할수 있다는 점임

첫 번째 단계는 그리드와 열 수를 정의하는 것
<div class="wrapper">
    <header>Header</header>

    <main> .... </main>

    <aside>Aside</aside>

    <footer>Footer</footer>
</div>
.wrapper {
  display: grid;
  grid-template-columns: 8fr 4fr; /* main element will take 8 and 4 cols, respectively. */
}
그리드 영역을 사용하려면 페이지의 각 요소(header, footer, main, aside)에 대해 이름을 지정해야 함.
header {
  grid-area: header;
}

footer {
  grid-area: footer;
}

main {
  grid-area: main;
}

aside {
  grid-area: aside;
}

.wrapper에서 해당 이름을 사용해 배치함

.wrapper {
  display: grid;
  grid-template-columns: 8fr 4fr; 
  grid-template-areas: "header header"
                       "main aside"
                       "footer footer";
}
  • 첫 번째 행의 header header는 2개의 열을 모두 사용하도록 함
  • 두 번째 행의 main과 aside는 각 1열을 사용하지만 비율이 2대 1임
  •  
    마지막 행의 footer footer는 2개의 열을 모두 사용

Repeat

같은 크기로 열, 행을 반복하려는 경우 repeat 키워드를 사용할 수 있음

.wrapper {
  display: grid;
  grid-template-columns: repeat(5, 100px);
}

Cards Layout

아래와 같은 카드 배치 레이아웃은 Grid가 적격임

media query 없이 쉽게 반응형을 구현할 수 있음

Card Layout

  • auto-fill을 사용하면 브라우저가 열, 행의 갯수를 자동으로 계산함
  • minmax(250px, 1fr)는 최소값은 250px, 최대값은 1fr을 의미함. 1열 1fr은 너비 100%를 의미
.wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}

헤더 요소

헤더 요소

아바타를 마크업의 맨 앞에 위치한 뒤, 순서만 바꿔보자

<ul>
  <li><a href="#"><img src="avatar.jpg" alt="" /></a></li>
  <li><a href="#">About</a></li>
  <li><a href="#">Works</a></li>
  <li><a href="#">Projects</a></li>
  <li><a href="#">Contact</a></li>
</ul>
아바타를 중앙으로 옮기기 위해서는 열의 시작과 끝을 알아야 함

ul {
    display: grid;
    grid-template-columns: repeat(5, 1fr); /* 1fr 1fr 1fr 1fr 1fr */
}

li {
    align-self: center; /* center the links vertically */
}

li:first-child {
    grid-column: 3 / 4;
}

문서 상 순서와 시각적 순서의 불일치는 스크린 리더 사용자의 혼란을 유발할 수 있으므로 조심해서 활용하자

더 읽을 자료

 

반응형