REBOUNDDesign System rebound-biz 기준 · 2026.09 저장소

개요

리바운드 비즈의 고객 페이지 — 채권자와 채무자가 실제로 쓰는 화면 — 를 만들 때 필요한 색·글자·컴포넌트 규칙이에요. 여기 적힌 값은 전부 rebound-biz 저장소에서 지금 돌고 있는 값이고, 새 화면은 이 값을 그대로 가져다 쓰면 됩니다.

이 문서를 쓰는 곳

제품저장소적용
리바운드 비즈GroundBasic/rebound-biz기준 — 이 문서에 적힌 모든 값의 출처
채무자 페이지rebound-biz /debtor같은 토큰, 같은 컴포넌트
신규 고객 페이지이 문서대로 시작
소개 페이지GroundBasic/rebound-info마케팅 톤은 별도 — 브랜드 색만 공유

네 가지 원칙

원칙
데이터가 주인공화면의 대부분은 무채색이에요. 색은 상태를 가리킬 때만 씁니다 — 표 한가득 알록달록한 배지는 읽는 속도를 떨어뜨려요.
채운 색은 하나한 화면에서 #FF5900으로 채운 버튼은 하나. 나머지 행동은 흰 버튼이나 고스트로 내립니다.
선보다 여백카드는 테두리 없이 흰 배경 + 아주 옅은 그림자. 선은 표의 행 사이와 영역 경계에만.
숫자는 줄을 맞춘다금액·건수·날짜는 고정폭 숫자(tabular-nums), 금액 열은 오른쪽 정렬.

문서의 구성

섹션무엇이 있나
Foundations팔레트 원본(Primitive)과 역할별 토큰(Semantic), 글자 스케일, 여백·모서리·그림자, 아이콘
Components버튼·배지·입력·카드·표·모달·내비 — 디자인 탭은 스펙과 규칙, 코드 탭은 실제 클래스 정의
Patterns한 화면을 구성하는 순서, 빈 화면·로딩·오류, 화면에 쓰는 말
Resources로고 파일, Tailwind 설정과 CSS 변수

설치와 사용

토큰 파일 두 개와 폰트 한 줄이면 준비가 끝나요. 새 저장소든, 이미 있는 페이지든 같습니다.

1. 설치

npm i -D tailwindcss postcss autoprefixer npx tailwindcss init -p

2. 토큰 가져오기

rebound-biz에서 두 파일을 그대로 복사합니다. 값은 Resources · 토큰에도 전문이 있어요.

파일가져올 부분
tailwind.config.jstheme.extend — colors · fontFamily · boxShadow · borderRadius
src/index.css@layer base(바탕·스크롤바·모바일 입력) + @layer components(버튼·입력·표·배지)

3. 폰트

@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css'); body { font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, system-ui, sans-serif; }

4. 화면 만들기 전 확인

확인기준
색을 골랐나요hex가 아니라 Semantic 토큰의 역할로. 팔레트에 없는 색은 만들지 않아요
주 행동은 하나인가요채운 오렌지 버튼은 화면에 하나
숫자 열은 오른쪽인가요금액·비율은 오른쪽 정렬 + 고정폭 숫자
빈 화면이 있나요데이터가 없을 때 무엇을 보여줄지 먼저 정하기
모바일에서 열리나요640px 아래에서 입력 글자 16px, 표는 가로 스크롤
색은 이 문서에서만 늘려요. 컴포넌트 안에 새 hex를 하드코딩하지 말고, 필요한 색이 없으면 여기에 추가한 뒤 쓰세요. 지금도 P3 디스플레이 렌더링 차이를 맞추려고 hex를 다시 덮어쓰는 규칙이 index.css 아래쪽에 있어요.

Primitive

팔레트 원본이에요. 화면에서 직접 부르는 일은 거의 없고, Semantic이 이 값을 가리킵니다. 색을 누르면 hex가 복사돼요.

Primary — 리바운드 오렌지

비즈의 메인 브랜드 색. 주 버튼, 활성 내비, 카운트 배지, 포커스 테두리에 씁니다.

Tertiary — 블루

Figma 디자인 시스템의 보조 블루. 정보·링크·신규 표시처럼 “행동이 아닌 안내”에 씁니다.

Neutral — 회색

화면의 90%는 이 회색들로 만들어져요. 바탕 다섯, 선 둘, 글자 셋.

Status

상환·회수 상태를 가리키는 네 가지. 배지의 점과 차트 계열색으로만 쓰고 배경을 넓게 칠하지 않아요.

이름 규칙

그룹단계비고
primary50 … 900Tailwind 기본 규칙(50이 가장 옅음). primary 단독은 500과 같아요.
tertiary5 … 90Figma 토큰 이름을 그대로 가져와서 단계 숫자가 달라요.
accent5 … 90개인 리바운드의 메인 오렌지 = 비즈의 보조. 값은 primary와 같습니다.
statusbad · normal · good · pending단계 없이 의미 이름만.

Semantic

“무슨 색인가”가 아니라 “무엇에 쓰는 색인가”로 정리한 목록이에요. 화면을 만들 때는 hex 대신 이 역할을 기준으로 고릅니다 — 같은 오렌지라도 행동에 쓸 때와 상태에 쓸 때의 뜻이 다르니까요.

Surface — 바탕

Content — 글자

Border — 선

Action — 행동

Status — 상태

정상 상환 연체 주의 이탈 신규 검토 중 종료
<span className="badge-active">정상 상환</span> {/* status.good */} <span className="badge-warn">연체 주의</span> {/* accent-50 */} <span className="badge-exit">이탈</span> {/* status.bad */}

고르는 순서

이렇게

  • 오렌지는 누를 수 있는 것지금 있는 위치에만
  • 상태는 status 네 가지 안에서 고르기
  • 정보·링크는 tertiary-50 블루
  • 바탕 회색은 surface / table-head / row-hover면 충분

이건 피해요

  • 제목을 오렌지로 칠하기 — 행동으로 오해돼요
  • 한 표 안에 배경 채운 배지 여러 색
  • 완료를 파랑으로, 안내를 초록으로 바꿔 쓰기
  • 팔레트에 없는 회색을 새로 만들기

Typography

Pretendard 한 벌만 씁니다. 크기를 키우기보다 굵기와 색으로 위계를 만들어요. 기본 줄 높이는 1.6, 큰 제목은 자간을 조입니다.

스케일

비즈 화면에서 가장 많이 쓰이는 크기는 13px이에요(고정 크기 지정의 약 40%). 새 화면도 13px을 기본으로 잡고 필요한 곳만 올리세요.

보기

규칙

상황규칙
숫자금액·건수·날짜는 tabular-nums. 표에서 자릿수가 흔들리지 않게.
금액12,340,000원 — 세 자리 쉼표, 단위는 붙여서. 축약(1.2천만)은 쓰지 않아요.
날짜2026.09.15, 기간은 2026.09.15 ~ 10.14, 남은 기간은 D-7.
줄바꿈한 줄로 끝나야 하는 셀은 truncate, 문장은 break-keep으로 단어를 자르지 않게.
모바일 입력iOS Safari는 16px 미만 입력에 포커스하면 화면을 확대해요. 640px 아래에서는 입력 글자를 16px로 강제합니다.
@media (max-width: 639px) { input, textarea, select { font-size: 16px !important; } }

Layout

여백은 4px 배수, 모서리는 크기에 비례, 그림자는 네 단계. 이 세 가지만 지켜도 화면의 결이 맞아요.

Spacing

토큰쓰는 곳
gap-14px배지 점과 글자, 아이콘과 숫자
gap-1.56px한 덩어리로 읽히는 인라인 요소
gap-28px기본 — 버튼 안, 버튼 사이, 아이콘+라벨
gap-312px리스트 행 사이, 폼 필드 사이
gap-416px카드 사이
space-y-520px페이지 섹션 사이
p-5 / p-620 / 24pxKPI 카드 / 일반 카드 안쪽
px-6 pt-624px모달 헤더·본문 좌우

Radius

토큰쓰는 곳
rounded-md6px툴팁, 아주 작은 칩
rounded-lg8px버튼, 입력, 내비 항목, 아이콘 박스
rounded-xl12px카드, 리스트 박스 — 가장 많이 씁니다
rounded-2xl12px모달. 설정에서 16px이 아니라 12px로 덮어썼어요
rounded-full999px카운트 배지, 아바타, 스텝 동그라미

Elevation

토큰쓰는 곳
shadow-card0 -2px 8px -2px rgba(16,24,40,.02),
0 6px 20px -6px rgba(16,24,40,.04),
0 2px 8px -2px rgba(16,24,40,.02)
카드 · KPI
shadow-md0 4px 8px -2px rgba(16,24,40,.08),
0 2px 4px -2px rgba(16,24,40,.06)
드롭다운 · 팝오버
shadow-xlTailwind 기본값모달
shadow-focus0 0 0 3px rgba(255,89,0,.25)키보드 포커스
입력칸은 포커스에서 그림자를 쓰지 않아요 — 테두리 색만 오렌지로 바뀝니다. 포커스 링은 키보드 이동 표시에만.

화면 폭

구간기준동작
모바일< 640px모바일 레이아웃 — 하단 탭, 입력 16px, 카드 1열
태블릿640 ~ 1023px사이드바 접힘, 표는 가로 스크롤
데스크톱≥ 1024px사이드바 256px 고정 + 본문

Iconography

아이콘은 lucide-react 하나만 씁니다. 선 굵기 2, 끝은 둥글게 — 기본값 그대로 쓰면 돼요.

크기

size빈도쓰는 곳
14두 번째로 많음표 안, 12~13px 글자 옆
16가장 많음 (기본)버튼, 내비, 리스트 행
18보통모달 닫기, 섹션 헤더
20드묾빈 화면, 강조

색과 자리

기본은 #a3a3a3, 마우스를 올리면 #161616. 내비에서는 #8A9099 → 활성 시 오렌지. 아이콘 단독 버튼은 28×28 rounded-lg 박스 안에 가운데로 둡니다.

import { Users } from 'lucide-react'; <div className="w-7 h-7 rounded-lg flex items-center justify-center text-text-muted"> <Users size={16} /> </div>

Button

세 가지면 충분해요. 채운 오렌지 하나, 흰 테두리 하나, 배경 없는 하나. 한 화면에서 채운 버튼은 하나만.

종류

클래스스펙쓰는 곳
.btn-primarypx-4 py-2 · 13/700 · r8
bg #FF5900 · 글자 흰색
화면의 주 행동 하나
.btn-outlinepx-4 py-2 · 13/600 · r8
border #E8E8E8 · shadow-sm
보조 행동, 내려받기·필터
.btn-ghostpx-2.5 py-2 · 13/500
배경 없음 · #a3a3a3
취소, 표 안 작은 행동
큰 CTApx-7 py-3 · 14/700 · r12빈 화면·온보딩의 단 하나의 시작 버튼

상태

상태표현
hoveropacity .9 — 색을 새로 정의하지 않고 투명도로 낮춰요.
activescale(.98) · 전환 200ms
disabledopacity .4 + cursor-not-allowed. 회색으로 바꾸지 않아요.
loading라벨을 “신청 중…”으로 바꾸고 비활성. 스피너는 아이콘 자리에.

라벨

이렇게

  • 위임 신청
  • 계좌 등록
  • 엑셀 내려받기
  • 동의하고 계속

이건 피해요

  • 확인 — 무엇을 확인하는지 모름
  • 제출하기 / ~하기 남발
  • OK, Submit 같은 영문
  • 느낌표 붙이기

클래스 정의

src/index.css → @layer components에 있는 원본이에요.

.btn-primary { @apply inline-flex items-center gap-2 px-4 py-2 text-sm font-bold rounded-lg hover:opacity-90 active:scale-[0.98] transition-all duration-200 cursor-pointer; background: #FF5900; color: #FFFFFF; } .btn-outline { @apply inline-flex items-center gap-2 px-4 py-2 border border-border bg-white text-text-secondary text-sm font-semibold rounded-lg hover:bg-surface hover:border-text-secondary/30 hover:text-text-primary active:scale-[0.98] transition-all duration-200 cursor-pointer shadow-sm; } .btn-ghost { @apply inline-flex items-center gap-2 px-2.5 py-2 text-text-muted text-sm font-medium rounded-lg hover:bg-surface hover:text-text-primary transition-all duration-200 cursor-pointer; }

쓰기

<button className="btn-primary" onClick={submit}>위임 신청</button> {/* 아이콘과 함께 — gap-2 는 클래스에 이미 들어 있어요 */} <button className="btn-outline"> <Download size={16} /> 엑셀 내려받기 </button> {/* 진행 중 */} <button className="btn-primary" disabled={loading}> {loading ? '신청 중…' : '위임 신청'} </button> {/* 큰 CTA — 빈 화면에서만 */} <button className="inline-flex items-center gap-2 px-7 py-3 bg-primary text-white text-sm font-bold rounded-xl hover:opacity-90 active:scale-[0.98] transition-all duration-200"> 채권 등록하기 </button>

주의

항목설명
background 인라인P3 디스플레이에서 bg-primary가 미묘하게 다르게 찍혀서 .btn-primary는 hex를 직접 넣었어요.
type폼 안의 버튼은 type="button"을 꼭 — 기본값이 submit이라 폼이 날아가요.
a 태그이동은 <Link className="btn-outline">로. 버튼처럼 보여도 링크는 링크로.

Badge

리바운드 비즈의 배지는 배경을 채우지 않아요. 색 점 하나 + 회색 글자 — 표 안에 수십 개가 늘어서도 시끄럽지 않게 하려는 선택이에요.

상태 배지

정상 상환 연체 주의 이탈 신규 검토 중 종료
클래스점 색쓰는 곳
.badge-activestatus.good정상 상환, 진행 중
.badge-warnaccent-50연체 주의, 확인 필요
.badge-exitstatus.bad이탈, 해지
.badge-newtertiary-30신규 등록
.badge-infotertiary-50검토 중, 안내
.badge-donetext-muted종료 — 글자까지 회색으로

공통 스펙 — inline-flex · gap-1.5 · 12px/600 · whitespace-nowrap, 점은 6px 원.

카운트 배지

내비나 탭의 미확인 개수. 여기서만 오렌지를 채웁니다. 99를 넘으면 99+.

채무자 관리 12 알림 99+

규칙

이렇게

  • 상태 하나당 배지 하나 — 한 행에 두 개까지
  • 끝난 것은 회색(.badge-done)으로 내리기
  • 글자는 상태 이름 그대로 — “정상 상환”

이건 피해요

  • 배경 채운 알약 배지 새로 만들기
  • 배지 안에 숫자와 글자 섞기
  • 테두리 두르기

클래스 정의

여섯 가지 모두 같은 틀이고 before:bg-*만 달라요.

.badge-active { @apply inline-flex items-center gap-1.5 text-xs font-semibold text-text-secondary whitespace-nowrap before:content-[''] before:inline-block before:w-1.5 before:h-1.5 before:rounded-full before:flex-shrink-0 before:bg-status-good; } /* 나머지는 점 색만 교체 */ .badge-warn → before:bg-accent-50 .badge-exit → before:bg-status-bad .badge-new → before:bg-tertiary-30 .badge-info → before:bg-tertiary-50 .badge-done → text-text-muted + before:bg-text-muted

쓰기

const BADGE = { ACTIVE: { cls: 'badge-active', label: '정상 상환' }, WARN: { cls: 'badge-warn', label: '연체 주의' }, EXIT: { cls: 'badge-exit', label: '이탈' }, DONE: { cls: 'badge-done', label: '종료' }, }; <span className={BADGE[status].cls}>{BADGE[status].label}</span> {/* 카운트 배지 */} <span className="inline-flex items-center justify-center min-w-[22px] h-[20px] px-1.5 rounded-full text-[11px] font-bold tabular-nums" style={{ background: '#FF5900', color: '#FFFFFF' }}> {count > 99 ? '99+' : count} </span>

Form

입력칸은 흰 바탕 + 얇은 테두리. 포커스하면 테두리만 오렌지로 바뀌고 그림자는 생기지 않아요.

입력

등록된 채권 정보와 같아야 해요

10자리 숫자만 입력해 주세요

스펙

요소스펙
.inputw-full · px-3.5 py-2.5 · 13px · r8 · border #E8E8E8 · bg #fff
:focusborder-color #FF5900 · outline:none · box-shadow:none
placeholder#a3a3a3 — 예시 값을 보여주고(“예) 김리바”), 지시문은 짧게
select기본 화살표를 지우고 #98A2B3 셰브런을 data-URI로. 오른쪽 여백 36px
라벨13px / 600, 아래 여백 6px. 선택 항목은 라벨 뒤에 (선택)
도움말 · 오류12px · #a3a3a3 / #EF4444. 둘을 동시에 보여주지 않아요
오류 문구는 무엇을 어떻게 고쳐야 하는지를 씁니다. “유효하지 않은 값입니다”가 아니라 “10자리 숫자만 입력해 주세요”.

클래스 정의

.input { @apply w-full px-3.5 py-2.5 text-sm border border-border rounded-lg bg-white placeholder:text-text-muted focus:outline-none focus:border-primary transition-[border-color] duration-150; outline: none; } .input:focus, .input:focus-visible { outline: none; box-shadow: none; } select.input { appearance: none; background-image: url("data:image/svg+xml,…chevron #98A2B3…"); background-repeat: no-repeat; background-position: right 12px center; padding-right: 2.25rem; } /* iOS Safari 자동 확대 방지 */ @media (max-width: 639px) { input, textarea, select { font-size: 16px !important; } }

쓰기

<div> <label htmlFor="name" className="block text-[13px] font-semibold mb-1.5"> 채무자 이름 </label> <input id="name" className="input" placeholder="예) 김리바" value={name} onChange={(e) => setName(e.target.value)} aria-invalid={!!error} /> {error ? <p className="text-[12px] text-status-bad mt-1.5">{error}</p> : <p className="text-[12px] text-text-muted mt-1.5">등록된 채권 정보와 같아야 해요</p>} </div> {/* 오류 상태는 테두리도 함께 */} <input className={`input ${error ? 'border-status-bad' : ''}`} />

Card

흰 바탕, 라운드 12, 테두리 없음. 그림자는 거의 보이지 않을 만큼 옅게 — 종이가 살짝 떠 있는 정도예요.

KPI 카드

회수 예정액12,340,000전월 대비 +8.2%
관리 중인 채무자148이번 달 신규 12
연체7확인 필요

스펙

클래스스펙쓰는 곳
.cardbg-white · r12 · p-6일반 콘텐츠 묶음
.kpi-cardbg-white · r12 · p-5
flex-col · gap-2
대시보드 지표 — 라벨 12, 값 26/800, 델타 배지
리스트 박스r12 · border #E8E8E8
divide-y
선택지·연락 수단처럼 행이 이어지는 목록

규칙

이렇게

  • 카드 사이 간격 16px
  • 카드 안에서 구분이 필요하면 가로선(divide-y)
  • KPI 값은 고정폭 숫자, 단위는 작게 붙여서

이건 피해요

  • 카드 안에 또 카드 — 회색 블록(#F7F8FA)으로 대신
  • 카드마다 다른 라운드·그림자
  • 테두리와 그림자를 같이 쓰기

클래스 정의

.card { @apply bg-white rounded-xl p-6; } .kpi-card { @apply bg-white rounded-xl p-5 flex flex-col gap-2; } /* 그림자가 필요하면 shadow-card 를 더합니다 */ <div className="card shadow-card">…</div>

쓰기

<div className="grid grid-cols-1 sm:grid-cols-3 gap-4"> <div className="kpi-card"> <span className="text-[12px] text-text-muted">회수 예정액</span> <span className="text-[26px] font-extrabold tracking-tight tabular-nums"> {format(amount)}원 </span> <span className="badge-active">전월 대비 +8.2%</span> </div> </div> {/* 카드 안 회색 블록 — 카드 안에 카드를 넣는 대신 */} <div className="card"> <div className="rounded-xl bg-[#F7F8FA] p-5">…</div> </div>

Table

채권자 화면의 중심이에요. 세로선 없이 가로선만, 머리글은 옅은 회색, 마지막 행의 아래선은 지웁니다.

보기

채무자원금상환률상태최근 입금
김리바8,400,00062%정상 상환2026.09.10
이바운3,200,00018%연체 주의2026.08.25
박그라12,000,000100%종료2026.07.30

스펙

클래스스펙
.table-wrapperbg-white · border-y #E8E8E8 · overflow-hidden — 좌우 테두리는 없어요
.thpx-5 py-3 · 14/600 · #a3a3a3 · bg #F5F6F8 · 가운데
.tdpx-5 py-3 · 15px · #737373 · 가운데 · 아래선 #E8E8E8
.tr-hoverhover:bg #F9FAFB · 행 전체가 링크면 cursor-pointer
마지막 행아래선 제거 — tbody tr:last-child td { border-bottom: 0 }
기본 정렬은 가운데지만, 금액·비율 열은 오른쪽 + 고정폭 숫자로 두세요. 자릿수가 다른 숫자가 가운데 정렬되면 읽기 어려워요.

비어 있을 때 · 많을 때

상황처리
결과 없음표 자리에 한 문장 — “조건에 맞는 채무자가 없어요.” + 필터 초기화 버튼
처음부터 없음표 대신 빈 화면 패턴
20건 초과페이지네이션. 무한 스크롤은 쓰지 않아요
좁은 화면가로 스크롤. 열을 숨기지 말고 첫 열(이름)을 고정

클래스 정의

.table-wrapper { @apply bg-white overflow-hidden border-y border-border; } .th { @apply px-5 py-3 text-center text-sm font-semibold text-text-muted whitespace-nowrap bg-[#F5F6F8] border-b border-border; } .td { @apply px-5 py-3 text-[15px] text-center text-text-secondary border-b border-border; } .tr-hover { @apply hover:bg-[#F9FAFB] transition-colors duration-150 cursor-pointer; } /* 가로 구분선만 — 마지막 행 하단선 제거 */ .table-wrapper tbody tr:last-child td { border-bottom: 0; }

쓰기

<div className="table-wrapper"> <div className="overflow-x-auto"> <table className="w-full"> <thead> <tr> <th className="th">채무자</th> <th className="th">원금</th> <th className="th">상태</th> </tr> </thead> <tbody> {rows.map((r) => ( <tr key={r.id} className="tr-hover" onClick={() => go(r.id)}> <td className="td">{r.name}</td> <td className="td text-right tabular-nums text-text-primary font-semibold"> {format(r.principal)} </td> <td className="td"><span className={BADGE[r.status].cls}>{…}</span></td> </tr> ))} </tbody> </table> </div> </div> {/* 결과 없음 */} {rows.length === 0 && ( <div className="py-14 text-center text-[13px] text-text-muted"> 조건에 맞는 채무자가 없어요. </div> )}

Modal

흐름을 끊어야 할 때만 띄웁니다. 성공 알림은 모달이 아니라 토스트로.

보기

계좌 등록

상환금을 받을 계좌를 등록해 주세요

스펙

영역스펙
배경bg-black/40 · 클릭하면 닫힘 · px-4로 모바일 좌우 여백
패널bg-white · rounded-2xl(12px) · shadow-xl · max-w-md(448px)
헤더px-6 pt-6 pb-2 · 제목 18/800 · 부제 14 회색 · 오른쪽 닫기(X 18)
본문px-6 pt-4 pb-6 · 항목 사이 16px
푸터오른쪽 정렬 · 간격 8px · 취소(outline) → 주 행동(primary) 순
폭 변형확인용 max-w-sm · 폼 max-w-md · 문서·동의서 max-w-2xl

규칙

이렇게

  • ESC와 바깥 클릭으로 닫히게
  • 제목은 무슨 일이 일어나는지 — “계좌 등록”
  • 되돌릴 수 없는 행동은 한 번 더 확인
  • 본문이 길어지면 헤더·푸터는 고정하고 가운데만 스크롤

이건 피해요

  • 모달 위에 모달
  • 성공 알림을 모달로 — 토스트로 충분해요
  • 닫기 없이 붙잡아 두기
  • 본문에 긴 안내문 밀어 넣기

구조

모달은 공용 클래스 없이 매번 같은 뼈대를 씁니다. 이 구조를 그대로 복사해 쓰세요.

export default function AccountModal({ open, onClose }) { if (!open) return null; return ( <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 px-4" onClick={onClose} > <div className="bg-white rounded-2xl shadow-xl w-full max-w-md" onClick={(e) => e.stopPropagation()} role="dialog" aria-modal="true" > {/* 헤더 */} <div className="flex items-start justify-between px-6 pt-6 pb-2"> <div> <h3 className="text-lg font-extrabold tracking-tight">계좌 등록</h3> <p className="text-sm text-text-muted mt-1">상환금을 받을 계좌를 등록해 주세요</p> </div> <button onClick={onClose} aria-label="닫기" className="p-1 rounded-lg text-text-muted hover:text-text-primary hover:bg-surface transition-colors flex-shrink-0"> <X size={18} /> </button> </div> {/* 본문 */} <div className="px-6 pt-4 pb-6 space-y-4">…</div> {/* 푸터 */} <div className="flex justify-end gap-2 px-6 pb-6"> <button className="btn-outline" onClick={onClose}>취소</button> <button className="btn-primary" onClick={submit}>등록</button> </div> </div> </div> ); }

ESC와 스크롤

useEffect(() => { if (!open) return; const onKey = (e) => { if (e.key === 'Escape') onClose(); }; document.addEventListener('keydown', onKey); document.body.style.overflow = 'hidden'; // 뒤 화면 스크롤 잠금 return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = ''; }; }, [open, onClose]);

Navigation

왼쪽 사이드바 256px 고정. 지금 있는 위치만 오렌지로, 나머지는 모두 회색이에요.

보기

채권 관리
대시보드
채무자 관리12
회수 내역
설정
설정

스펙

요소스펙
사이드바256px · bg #fff · 오른쪽 선 #E8E8E8 · 로고 영역 높이 80px
항목px-3 py-2 · r8 · 14px — 기본 500/#52525b, 활성 600/#FF5900 + 배경 #F4F4F5
권한 없음opacity .4 · cursor-not-allowed + 오른쪽 툴팁으로 이유 설명
그룹 라벨11px / 600 · uppercase · tracking-wider · #a1a1aa
모바일사이드바 대신 하단 탭. 활성 색 규칙은 같아요

항목

<NavLink to={to} end={end} className={({ isActive }) => [ 'flex items-center gap-2.5 px-3 py-2 rounded-lg text-[14px]', 'transition-all duration-150 cursor-pointer', isActive ? 'font-semibold' : 'font-medium hover:bg-black/[0.04]', ].join(' ')} style={({ isActive }) => ({ color: isActive ? '#FF5900' : '#52525b', background: isActive ? '#F4F4F5' : undefined, })} > {({ isActive }) => ( <> <div className="w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0" style={{ color: isActive ? '#FF5900' : '#8A9099' }}> <Icon size={16} /> </div> <span className="truncate flex-1">{label}</span> {count > 0 && ( <span className="min-w-[22px] h-[20px] px-1.5 rounded-full text-[11px] font-bold tabular-nums inline-flex items-center justify-center" style={{ background: '#FF5900', color: '#FFFFFF' }}> {count > 99 ? '99+' : count} </span> )} </> )} </NavLink>

껍데기

<aside className="w-[256px] flex-shrink-0 flex flex-col h-full overflow-hidden border-r border-border bg-white"> <div className="h-[80px] flex items-center gap-2.5 px-5 flex-shrink-0"><Logo /></div> <nav className="flex-1 min-h-0 py-4 px-3 space-y-1 overflow-y-auto"> <p className="px-3 pt-2 pb-1 text-[11px] font-semibold uppercase tracking-wider" style={{ color: '#a1a1aa' }}>채권 관리</p> … </nav> </aside>

페이지 구성

모든 화면은 같은 순서로 시작해요. 제목 → 한 줄 설명 → 요약 → 목록.

페이지 헤더

회수 내역
CMS 출금 기반 상환 이행 현황
<h1 className="page-title">회수 내역</h1> <p className="page-subtitle">CMS 출금 기반 상환 이행 현황</p> .page-title text-[32px] font-bold text-text-primary tracking-tight .page-subtitle text-[15px] text-text-secondary mt-1

섹션 사이 간격은 20px. 인사형 제목은 “안녕하세요, 김리바님” + 오늘 날짜를 부제로 둡니다.

빈 화면

처음 온 사용자가 가장 먼저 보는 화면이에요. 사과하지 말고, 다음에 할 일 하나만 크게 보여줍니다.

아직 등록된 채권이 없습니다

첫 채권을 등록하면 상환 일정과 회수 현황을 자동으로 따라가요.

제목 22/800 tracking-tight 설명 14px text-secondary · 최대 500px · 가운데 CTA px-7 py-3 r12 · 화면에 단 하나 아래 3단계 안내 카드(bg #F7F8FA · r12 · p-5)를 나란히

상태별 화면

상태표현
로딩스켈레톤(회색 #F4F4F5 블록). 스피너는 버튼 안에서만
빈 결과한 문장 + 되돌리는 행동(필터 초기화)
오류무슨 일이 있었는지 + 다시 시도. 코드·스택은 보여주지 않아요
권한 없음메뉴는 보이되 흐리게 + 툴팁으로 이유 — “관리자만 신청할 수 있어요”
성공토스트 한 줄. 화면을 막지 않아요

문구

돈과 법이 걸린 화면이에요. 친근함보다 정확함이 먼저고, 정확한 다음에 짧게 씁니다.

기본

항목규칙
말투존댓말 해요체 — “등록되었습니다”보다 “등록했어요”. 단, 법적 고지·동의서는 문어체 그대로
용어채권자 / 채무자 / 상환 / 회수 / 위임 / 연체 — 화면마다 다르게 부르지 않아요
주어사용자가 한 일은 사용자 기준으로. “시스템이 처리했습니다” ✕
길이버튼 2~6자, 설명 한 문장, 도움말 두 줄 이내

숫자 표기

대상표기비고
금액12,340,000원축약·소수점 없이
건수148명 · 7건단위 붙여쓰기
비율62%필요할 때만 소수점 한 자리(62.5%)
날짜2026.09.15요일이 필요하면 2026.09.15 (화)
기간2026.09.15 ~ 10.14같은 해면 뒤쪽 연도 생략
남은 기간D-7당일은 D-day

예시

이렇게

  • 계좌를 등록하면 상환금이 자동으로 들어와요.
  • 9월 25일에 출금될 예정이에요.
  • 10자리 숫자만 입력해 주세요.
  • 관리자만 신청할 수 있어요.

이건 피해요

  • 처리 중 오류가 발생하였습니다 (E-500)
  • 유효하지 않은 값입니다
  • 정말로 삭제하시겠습니까?!
  • Loading… 처럼 영문 섞기

로고

심볼과 워드마크, 그리고 쓰면 안 되는 방식이에요.

파일

리바운드 로고 리바운드 심볼
심볼 54×55 · #FF5900 · 투명도 0.35 / 0.45 / 0.5 / 0.8 / 1 다섯 조각 워드마크 145×44 · 기본 #0B1C2C · 어두운 배경에서는 #FFFFFF 조합 심볼 : 워드마크 = 34 : 102, 사이 간격 10px

규칙

규칙내용
최소 크기심볼 20px, 조합 로고 높이 24px 이상
여백사방으로 심볼 높이의 절반 이상 비워요
심볼은 항상 #FF5900. 워드마크만 배경에 따라 #0B1C2C / #FFFFFF
금지비율 변경, 그림자, 그라디언트, 심볼 회전, 임의 재배치

코드로 쓰기

{/* rebound-biz / src/components/Logo.tsx */} <Logo /> {/* 기본 — 심볼 + 워드마크(#0B1C2C) */} <Logo wordmarkColor="#FFFFFF" /> {/* 어두운 배경 */} <Logo markOnly /> {/* 심볼만 — 모바일 헤더, 파비콘 */} <Logo scale={0.8} /> {/* 크기 배율 */}

토큰

새 프로젝트에 그대로 붙여 넣을 정의예요. 값은 Primitive와 같습니다.

Tailwind 설정

CSS 변수

Tailwind를 쓰지 않는 페이지(정적 HTML, 이메일 템플릿)에서는 이 변수만 복사해 쓰세요.

어디에 있나

항목위치
토큰 원본rebound-biz / tailwind.config.js
컴포넌트 클래스rebound-biz / src/index.css → @layer components
화면 카탈로그rebound-biz / design — 전체 라우트와 모달을 시나리오별로 확인
아이콘lucide-react
폰트Pretendard v1.3.9 (jsdelivr static)