블롭·일러스트 없이 빈 화면 채우기: 데코 없는 empty state 컴포넌트
데코 금지 브랜드에서 그림 없이 빈 목록 화면을 설계하는 법. 상태 3종 분리, 3조각 컴포넌트, 틴트·1px 보더·8px 간격으로 채우는 구현을 정리해요.
빈 목록이 뜨는 순간, 손이 자꾸 일러스트로 가요. 둥근 블롭 하나, 빈 상자 그림 하나 얹으면 허전함이 사라지니까요. 그런데 데코를 아예 금지한 브랜드 규칙 아래서 플랜비(plan-b)의 멤버·회비 화면을 만들다 보니, 그림 없이도 빈 화면이 충분히 설명적일 수 있더라고요. 그 구현 방식을 정리해 볼게요.
빈 화면은 하나가 아니에요
직접 만들어 보니 "비어 있음"은 최소 3가지로 갈라져요. 아직 아무것도 없는 초기 빈 상태, 필터 때문에 결과가 0건인 필터 빈 상태, 요청이 실패한 오류 상태예요. 셋을 한 컴포넌트로 뭉개면 "멤버가 없어요"라는 문구가 필터 걸린 화면에도 떠서, 사용자는 데이터가 사라진 줄 알아요. 그래서 variant 하나로 갈라 두고 문구와 버튼을 각각 받게 했어요.
초기 빈 상태 · 필터 빈 상태 · 오류 상태 — 문구와 버튼이 어떻게 달라지는지
컴포넌트는 세 조각이면 돼요
제목, 설명 한 줄, 버튼 1개. 이 셋만 받아요. 버튼을 여러 개 받도록 열어 두면 나중에 꼭 "취소"나 "더 알아보기"가 붙어서 빈 화면이 메뉴판이 돼요. 화면당 CTA 1개라는 규칙을 props 타입으로 못 박아 두는 편이 마음 편했어요.
type EmptyStateProps = {
variant: "initial" | "filtered" | "error";
title: string;
description: string;
action: { label: string; onClick: () => void };
};
export function EmptyState({ title, description, action }: EmptyStateProps) {
return (
<section role="status" className="empty-state">
<h2>{title}</h2>
<p>{description}</p>
<button type="button" onClick={action.onClick}>{action.label}</button>
</section>
);
}role="status"를 붙여 두면 목록이 비는 순간 스크린리더가 문구를 읽어 줘요. 텍스트가 주인공이라 접근성은 오히려 챙기기 쉬웠어요.
허전함은 여백과 면으로 메워요
데코를 걷어내면 남는 건 배경 틴트, 둘레 1px 보더, 간격이에요. 적용해 봤더니 이 세 가지만으로 "여기가 비어 있는 자리"라는 게 분명해졌어요.
.empty-state {
padding: 48px 24px;
border: 1px solid color-mix(in oklch, var(--color-on-surface) 12%, transparent);
background: color-mix(in oklch, var(--color-primary) 4%, var(--color-surface));
text-align: left;
}
.empty-state h2 + p { margin-top: 8px; }
.empty-state p + button { margin-top: 24px; }간격은 전부 8px 배수예요. 제목과 설명 사이 8px, 설명과 버튼 사이 24px. 테스트해 보면 13px 같은 임의값이 하나만 섞여도 화면이 묘하게 어색해져요. 배경은 브랜드색을 4%만 섞어 강조가 아니라 영역으로 읽히게 했고, 가운데 정렬 대신 왼쪽 정렬로 본문 톤과 맞췄어요.
- 둘레 1px 보더
- 브랜드색 4% 틴트
- 8px 배수 간격
- 버튼 1개
레이아웃 흔들림도 같이 잡아요
빈 화면을 목록과 다른 자리에 렌더링하면 데이터가 들어오는 순간 화면이 튀어요. 돌려 보니 리스트 컨테이너 안에 그대로 넣고 min-height를 목록 3행 높이로 맞추는 게 제일 단순했어요. 로딩 스켈레톤도 같은 높이를 쓰니 로딩, 빈 상태, 데이터 순서로 바뀔 때 점프가 사라졌어요.
정리
그림을 못 쓴다는 제약이 처음엔 손발을 묶는 것 같았는데, 결과적으로 문구를 더 고민하게 만들었어요. "멤버가 없어요"보다 "첫 멤버를 초대해 보세요"가 낫고, 버튼은 그 문장의 동사를 그대로 받아요. 빈 화면에 필요한 건 채우는 그림이 아니라 다음 행동 하나예요.
관련 글
댓글
아직 댓글이 없어요. 첫 댓글을 남겨주세요.