피그마 화면과 브라우저 화면은 쌍둥이처럼 보여도 서로 다른 엔진 위에서 그려집니다. 그래서 픽셀을 아무리 맞춰도 미세하게 어긋나는 순간이 옵니다. 차이를 없애려 애쓰기보다, 차이를 견디는 CSS를 짜는 쪽이 훨씬 편합니다.
73.1 픽셀 퍼펙트 신화
피그마의 글자는 자체 렌더러로 그려지고, 브라우저 글자는 OS의 폰트 힌팅을 거칩니다. 같은 폰트, 같은 크기여도 한두 픽셀은 늘 어긋납니다.
/* 피그마 수치를 그대로 못 박은 예 */
.title {
font-size: 24px;
line-height: 29px; /* 피그마가 알려준 값 */
letter-spacing: -0.3px;
}
line-height를 px로 못 박으면 폰트가 바뀌거나 확대될 때 줄 간격이 어색하게 벌어집니다. 비율로 바꾸면 크기를 따라옵니다.
/* 비율로 옮긴 예 */
.title {
font-size: 1.5rem; /* 24px 기준 */
line-height: 1.2; /* 29 / 24 반올림 */
letter-spacing: -0.01em;
}
letter-spacing도 px 대신 em으로 두면 글자 크기와 함께 늘고 줄어 관계가 유지됩니다.
73.2 고정 px에서 유연 단위로
피그마는 모든 값을 px로 내보냅니다. 그 숫자를 그대로 옮기면 사용자 글자 확대 설정을 무시하게 됩니다.
/* 나쁜 예: 확대에 반응하지 않음 */
.card {
width: 320px;
padding: 16px;
font-size: 14px;
}
같은 화면을 rem과 유연 폭으로 옮기면 접근성 설정과 좁은 화면을 함께 견딥니다.
/* 좋은 예: 견디는 카드 */
.card {
width: 100%;
max-width: 20rem; /* 320px 상한만 */
padding: 1rem;
font-size: 0.875rem;
}
폭을 상한으로만 두면 부모가 좁아질 때 카드가 알아서 줄어듭니다.
73.3 그림자와 블러 근사
피그마의 그림자는 자체 계산식이라 box-shadow로 100퍼센트 재현되지 않습니다. 값을 그대로 베끼지 말고 브라우저에서 눈으로 맞춰야 합니다.
.panel {
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.06),
0 4px 12px rgba(0, 0, 0, 0.08);
}
그림자를 얇은 것과 넓은 것 두 겹으로 나누면 피그마의 부드러운 번짐에 더 가깝게 다가섭니다. 배경 흐림이 필요할 때는 backdrop-filter를 씁니다.
.overlay {
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(8px);
}
피그마의 layer blur는 대략 이 정도 흐림으로 옮겨오면 자연스럽습니다.
73.4 피그마엔 없는 긴 텍스트
디자인 시안의 이름은 늘 짧고 예쁩니다. 실제 데이터는 두 배로 길게 들어와 레이아웃을 밀어냅니다. 한 줄 자름부터 준비합니다.
.name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
여러 줄을 허용하되 어느 줄에서 자르고 싶다면 line-clamp를 씁니다.
.excerpt {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
세 줄까지 보여주고 말줄임표로 마무리해, 긴 본문이 카드 높이를 무너뜨리지 않습니다.
73.5 시안 재현과 실환경 견딤
같은 목록 항목을 두 방식으로 비교하면 차이가 분명해집니다.
/* 나쁜 예: 시안 그대로, 넘침 무방비 */
.item {
width: 280px;
height: 40px;
font-size: 15px;
}
이 항목은 긴 제목이 들어오면 40px 높이를 뚫고 글자가 삐져나옵니다.
/* 좋은 예: 넘침과 확대를 견딤 */
.item {
width: 100%;
min-height: 2.5rem;
padding: 0.5rem 0.75rem;
font-size: 0.9375rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
높이를 min-height로 열어두고 넘침을 미리 막으면, 어떤 데이터가 와도 항목이 흐트러지지 않습니다.
73.6 서브픽셀과 반올림
피그마에서 39px 높이를 정확히 3등분하면 13px씩입니다. 하지만 브라우저는 부모 높이가 홀수거나 소수일 때 각 칸을 다르게 반올림해, 한 칸만 1px 두꺼워 보이기도 합니다.
/* 나쁜 예: 소수 높이가 어긋남을 부름 */
.row {
height: 39px;
}
.cell {
height: 13px; /* 3칸 합이 딱 맞길 기대 */
}
높이를 못 박는 대신 flex로 공간을 나누면, 브라우저가 남는 픽셀을 알아서 분배합니다.
/* 좋은 예: 나눗셈을 브라우저에 맡김 */
.row {
display: flex;
flex-direction: column;
}
.cell {
flex: 1;
}
테두리도 마찬가지입니다. 1px 선을 두 요소가 각각 그리면 맞닿는 곳이 2px로 겹쳐 보입니다. 한쪽만 그려 겹침을 막습니다.
.list-item {
border-bottom: 1px solid #e5e7eb;
}
.list-item:last-child {
border-bottom: none;
}
마지막 항목의 아래 선만 지우면, 목록 끝이 컨테이너 테두리와 겹쳐 굵어지는 일이 없습니다.
73.7 확인할 것과 넘어갈 것
모든 픽셀을 붙들면 정작 중요한 어긋남을 고칠 힘이 흩어집니다. 위계와 정렬은 엄격히, 렌더링에서 온 미세한 차이는 너그럽게 봅니다.
/* 정렬은 값으로 못 박아 지킴 */
.toolbar {
display: flex;
align-items: center;
gap: 12px;
}
정렬 기준을 값으로 고정해 두면 폰트 렌더링이 조금 달라져도 축이 흔들리지 않습니다. 지킬 것을 지켜두면 나머지 미세한 차이는 눈감아도 화면의 뜻이 살아 있습니다.
73.8 콘텐츠 폭에 갇힌 버튼
시안에서 버튼은 늘 딱 맞는 라벨을 달고 있습니다. 실제로는 짧은 라벨과 긴 라벨이 섞여 들어와 버튼 크기가 제각각이 됩니다.
/* 나쁜 예: 라벨 길이에 끌려다님 */
.btn {
padding: 8px 12px;
}
최소 폭을 정해 짧은 라벨도 옹색해 보이지 않게 하고, 좌우 여백은 넉넉히 둡니다.
/* 좋은 예: 최소 폭과 가운데 정렬 */
.btn {
min-width: 96px;
padding: 0.5rem 1rem;
display: inline-flex;
align-items: center;
justify-content: center;
}
확인과 취소처럼 나란히 놓이는 버튼이 라벨 길이와 상관없이 비슷한 덩치를 유지해, 줄이 가지런해 보입니다.
73.9 정리
차이를 지우려 밤을 새우기보다, 비율 단위와 넘침 처리로 차이를 품는 편이 오래 갑니다. 피그마는 출발점이고, 마지막 조율은 늘 브라우저 앞에서 이뤄집니다.