[CSS] 실전 Grid Layout 활용
원문 : https://ishadeed.com/article/css-grid/
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>
항목 사이에는 간격이 없음
수직 간격과 수평 간격
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;
}
행의 높이와 갯수를 명시적으로 적어줄 수도 있음
.wrapper {
grid-template-columns: 200px 200px 200px;
grid-template-rows: 100px 100px; /* implicitly added by the browser */
}
예제 1 : 뉴스 리스트 컴포넌트
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)
그리드는 그리드 컨테이너의 공간을 그리드 항목(그리드 컨테이너의 컨텐츠을 나타냄)이 배치될 수 있는 그리드 영역으로 나누는
교차하는 수평 및 수직 그리드 선 집합입니다.
Grid에는 열과 행 모두에 그리드 선(grid line) 개념이 존재함
아래 그림에는 3개의 수직 선이 존재
첫번쨰 항목의 행 방향 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개의 행이 아닌 열을 차지하도록 해보자
2개의 열을 차지하는 CSS
.item:first-child {
grid-column-start: 1;
grid-column-end: 3;
}
속기 버전
.item:first-child {
grid-column: 1 / 3;
}
배치 결과는 다음과 같음
예제 2 : 회원 가입 양식
아래와 같은 양식을 Grid로 구현해 보기
요구 사항
- 각 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
요구 사항
- 인풋의 왼쪽에 정렬된 라벨
- 인풋과 정렬된 버튼
이 경우는 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)을 적용함
form {
display: grid;
grid-template-columns: 1fr 2fr;
grid-gap: 16px;
}
button {
grid-column: 2 / 3;
}
그리드 영역(Grid Areas)
그리드 영역은 테이블의 cell로 생각할 수 있음
각 셀은 4면으로 둘러쌓여 있고, 그리드 영역도 동일함
그리드 영역의 좋은 점은 각 영역에 이름을 붙이고 각 요소를 해당 영역에 배치할수 있다는 점임
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 {
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 없이 쉽게 반응형을 구현할 수 있음
- 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;
}
문서 상 순서와 시각적 순서의 불일치는 스크린 리더 사용자의 혼란을 유발할 수 있으므로 조심해서 활용하자