피그마의 프레임은 폭이 정해진 방이지만, 웹 화면은 그릇에 따라 모양이 변하는 물과 같습니다. 저는 그 물이 흐를 길을 코드로 열어 둡니다.
71.1 브레이크포인트는 미디어 쿼리로
피그마에서 좁은 프레임과 넓은 프레임을 따로 그렸다면, 그 경계가 코드에서는 미디어 쿼리가 됩니다.
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 768px) {
.grid {
grid-template-columns: 1fr 1fr;
}
}
좁을 때는 한 칸, 768px부터는 두 칸입니다. 좁은 화면을 기본으로 두고 넓어질 때 규칙을 더하는 모바일 우선 방식입니다.
기준을 넓혀 갈수록 min-width 조건을 쌓아 올리면 됩니다.
@media (min-width: 1024px) {
.grid {
grid-template-columns: 1fr 1fr 1fr;
}
}
넓은 화면에서 세 칸으로 펼쳐집니다. 조건이 겹겹이 쌓여 작은 화면부터 큰 화면까지 이어집니다.
71.2 고정 px 대신 유동 단위
고정 폭은 화면이 좁아지면 그대로 넘칩니다. 가로 스크롤이 생기는 흔한 원인입니다.
.panel {
width: 960px;
}
화면이 960px보다 좁으면 패널이 밖으로 삐져나갑니다. 폭을 비율과 상한으로 바꾸면 좁은 곳에서도 얌전히 줄어듭니다.
.panel {
width: 100%;
max-width: 960px;
}
넓은 화면에서는 960px까지만 넓어지고, 좁은 화면에서는 화면 폭에 맞춰 줄어듭니다.
71.3 clamp로 유동 타이포
제목 크기를 기기마다 미디어 쿼리로 일일이 바꾸는 대신, 한 줄로 최소와 최대 사이를 흐르게 할 수 있습니다.
.title {
font-size: clamp(1.5rem, 5vw, 2.5rem);
}
.section {
padding: clamp(16px, 4vw, 48px);
}
clamp는 최솟값, 유동값, 최댓값을 받습니다. 화면이 좁으면 1.5rem 아래로는 내려가지 않고, 넓으면 2.5rem에서 멈춥니다. 그 사이에서는 화면 폭을 따라 부드럽게 자랍니다.
71.4 유동 카드 그리드
카드 개수가 화면 폭에 따라 스스로 바뀌게 하면, 미디어 쿼리 없이도 반응합니다.
.cards {
display: grid;
grid-template-columns:
repeat(auto-fit, minmax(240px, 1fr));
gap: 20px;
}
auto-fit은 들어갈 수 있는 만큼 칸을 만들고, minmax는 칸이 240px보다 좁아지지 않게 지킵니다. 넓으면 네댓 칸, 좁으면 한 칸으로 카드가 알아서 접힙니다.
카드 안의 내용도 위아래로 고르게 펴 두면 높이가 들쭉날쭉하지 않습니다.
.card {
display: flex;
flex-direction: column;
height: 100%;
}
.card__footer {
margin-top: auto;
}
margin-top을 auto로 두면 바닥 영역이 카드 아래로 밀려, 내용 길이가 달라도 버튼 줄이 나란히 맞습니다.
71.5 flex로 넘치면 다음 줄로
가로로 나란한 요소가 좁은 화면에서 넘칠 때는, 다음 줄로 흘려보냅니다.
.toolbar {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.toolbar > * {
flex: 1 1 160px;
}
flex-wrap이 wrap이면 자리가 부족할 때 요소가 아랫줄로 내려갑니다. flex 1 1 160px은 기준 폭 160px에서 남는 공간을 나눠 갖게 합니다.
71.6 컨테이너로 폭 가두기
넓은 화면에서 글이 한 줄로 끝없이 늘어지면 읽기 어렵습니다. 본문 폭에 상한을 두고 가운데로 모읍니다.
.container {
width: 100%;
max-width: 1140px;
margin: 0 auto;
padding: 0 16px;
}
margin을 auto로 두면 남는 좌우 여백이 똑같이 나뉘어 가운데로 모입니다. 좌우 padding은 좁은 화면에서 글이 벽에 붙지 않게 지켜 줍니다.
71.7 좁은 화면에서 접기와 펼치기
넓은 화면에서 곁에 나란하던 메뉴가 좁은 화면에서는 세로로 쌓여야 할 때가 있습니다. 방향만 바꾸면 됩니다.
.nav {
display: flex;
flex-direction: column;
gap: 8px;
}
@media (min-width: 768px) {
.nav {
flex-direction: row;
gap: 24px;
}
}
좁을 때는 세로로 쌓이고 768px부터는 가로로 펼쳐집니다. 순서나 내용은 그대로 두고 흐르는 방향만 바꾼 것입니다.
71.8 세로로 쌓을 때 순서 바꾸기
좁은 화면에서 세로로 쌓이면, 넓은 화면에서 오른쪽에 있던 중요한 내용이 아래로 밀릴 수 있습니다. 순서를 다시 정합니다.
.layout {
display: flex;
flex-direction: column;
}
.main { order: 1; }
.side { order: 2; }
@media (min-width: 1024px) {
.layout { flex-direction: row; }
.side { order: 0; }
}
좁은 화면에서는 본문을 먼저 보이고 곁가지를 뒤로 미룹니다. 넓어지면 곁가지가 다시 왼쪽으로 돌아옵니다. 사용자가 중요한 것을 찾아 한참 내리지 않아도 됩니다.
71.9 이미지 비율 지키기
넓은 화면에서 시원하던 가로로 긴 사진이 좁은 화면에서 너무 납작해지곤 합니다. 비율을 고정하고 넘치는 부분은 잘라 담습니다.
.thumb {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
aspect-ratio가 폭이 어떻든 16 대 9 비율을 지키고, object-fit cover가 빈틈 없이 채우며 넘치는 가장자리만 잘라 냅니다. 어느 너비에서도 사진이 일그러지지 않습니다.
71.10 손가락을 위한 여백
좁은 화면은 무딘 손가락으로 누릅니다. 누를 것을 넉넉히 키우고 사이를 벌려, 옆의 것이 잘못 눌리지 않게 합니다.
.tap {
min-height: 44px;
min-width: 44px;
padding: 12px 16px;
}
@media (min-width: 1024px) {
.tap {
min-height: 36px;
padding: 8px 12px;
}
}
좁은 화면에서는 최소 44px의 누를 자리를 두고, 마우스를 쓰는 넓은 화면에서는 조금 줄입니다. 같은 버튼도 다루는 손을 헤아려 크기를 바꿉니다.
71.11 물이 흐를 길
고정 px를 비율과 clamp로, 낱개의 폭을 auto-fit 그리드로 바꾸면, 제가 그리지 않은 무수한 중간 너비에서도 화면이 알아서 흐릅니다.
img {
max-width: 100%;
height: auto;
}
이 한 규칙만으로 모든 이미지가 부모 폭을 넘지 않고 비율을 지키며 줄어듭니다.
마지막으로 어떤 요소도 화면 밖으로 삐져나가지 않도록 밑바닥에 안전망을 깔아 둡니다.
html, body {
overflow-x: hidden;
}
* {
min-width: 0;
}
min-width를 0으로 풀면 긴 글이 flex 칸을 밀어 넘치는 일을 막습니다. 반응형은 그릇을 존중하는 물의 마음입니다.