# Design System — Claymorphism (클레이)

> 파스텔 바탕 위에 찰흙을 빚어 부풀린 듯한 둥근 카드와 말랑한 버튼을 올리는 스타일. 2021년 무렵 Dribbble에서 퍼진 뒤 온보딩 앱, 교육·커뮤니티 서비스, 이벤트 페이지에서 자주 쓰인다.
> 공통 규칙은 `anti-slop.md`를 함께 적용한다. 미리보기: `preview/claymorphism.html` (신입사원 온보딩 앱 예시)

> **장식 제한 완화 범위** — 이 묶음(트렌디 · 감성)은 anti-slop.md의 "그라디언트 금지 · 효과 1종 · 그림자 토큰 최소화"를 **이 문서 5장이 정한 범위 안에서만** 풀어 준다. 허용되는 것은 ① 클레이 그림자 레시피 3단계(`--clay-lg`·`--clay-md`·`--clay-pressed`) ② 버튼 표면의 위→아래 한 방향 미세 그라디언트 ③ 배경의 파스텔 단색 면 2~3개뿐이다. 그 밖의 anti-slop 원칙(이모지 금지, 현실적인 한국어 더미 데이터, 아이콘 카드 3개 나열 금지, 빈 상태·로딩·에러 디자인)은 그대로 지킨다.

---

## 1. 분위기

말랑하고 손에 쥐고 싶은 느낌이다. 모든 덩어리가 **바깥으로는 부드럽게 떠 있고, 안쪽으로는 빛을 머금어 볼록하다.** 뉴모피즘(neumorphism)이 바탕과 같은 색으로 눌러 판 "음각"이라면, 클레이는 바탕과 **다른 색**의 덩어리를 바탕 위에 "얹은" 양각이다. 그래서 카드가 배경에 묻히지 않고 경계가 분명하다.

색은 파스텔 세 가지(라벤더·피치·민트)가 주인공이고, 글자는 그 위에서 충분히 진한 자두색(plum)으로 눌러 쓴다. 반경은 크다(카드 28px, 버튼 18px). 모서리가 날카로운 요소는 하나도 없다. 아이콘도 굵은 선(2.2px)과 둥근 끝으로 맞춘다.

**어울리는 곳**
- 신입사원 온보딩, 사내 교육 진도, 동호회·봉사활동 모집, 사내 이벤트(창립기념일, 체육대회) 안내
- 체크리스트·진행률·배지처럼 "해냈다"를 보여줄 일이 많은 화면
- 처음 쓰는 사람이 긴장을 풀어야 하는 화면 (입사 첫 주, 첫 설문)

**쓰면 안 되는 곳**
- GMP 문서, 일탈·CAPA 보고, 품질 기록처럼 **신뢰와 엄격함**이 우선인 화면
- 표가 20행 넘는 데이터 화면, 재무·구매 대시보드 — 큰 반경과 두꺼운 그림자가 밀도를 다 잡아먹는다
- 의약품 정보, 환자·개인정보 관련 안내 — 장난스럽게 보이면 내용이 가볍게 읽힌다
- 경영진 보고 자료

**핵심 특징**
- 파스텔 바탕 + 바탕보다 밝은 덩어리(카드), 덩어리마다 4겹 클레이 그림자
- 반경 3단계만: 카드 28px / 내부 블록 20px / 버튼·입력 18px (알약은 999px)
- 버튼은 눌렀을 때 실제로 들어간다 (`--clay-pressed` + 1px 하강)
- 글자는 진한 자두색, 파스텔 위에 회색 글자를 쓰지 않는다
- 숫자·영문은 둥근 Fredoka, 한글 본문은 Pretendard, 큰 제목만 둥근 한글 Jua

---

## 2. 컬러 팔레트 & 역할

### Light (기본)

```css
:root {
  /* 바탕 — 파스텔 3색 */
  --bg:           #ECE6FB;  /* 라벤더 페이지 바탕 */
  --bg-peach:     #FFE3D3;  /* 피치 면 (배너, 강조 카드 바탕) */
  --bg-mint:      #D3F2E4;  /* 민트 면 (완료·성공 블록 바탕) */

  /* 덩어리 */
  --surface:      #F8F5FF;  /* 기본 카드 (바탕보다 밝게) */
  --surface-2:    #EFE9FD;  /* 카드 안 가라앉은 블록, 진행 트랙 */

  /* 글자 */
  --text:         #2A2342;  /* 자두색 본문 — 바탕 대비 12:1 */
  --muted:        #5B5276;  /* 보조 글자 — surface 대비 6.9:1, bg 대비 6.2:1 */
  --border:       #DCD3F4;  /* 입력·구분선 */

  /* 동작 */
  --primary:      #6A4FD6;  /* 주 버튼 (흰 글자 5.6:1) */
  --primary-deep: #5639C2;  /* 주 버튼 아래쪽 / 눌림 */
  --accent:       #F08A5D;  /* 피치 진한 색 — 진행률·오늘 표시 '지금' 한 곳 */
  --accent-text:  #9A3F17;  /* 피치 면 위 글자 (bg-peach 대비 6.1:1) */
  --mint-text:    #1D6B4D;  /* 민트 면 위 글자 (bg-mint 대비 6.0:1) */
  --danger:       #C23B4E;  /* 오류 글자 */

  --btn-text:     #FFFFFF;
}
```

### Dark

다크에서는 덩어리가 바탕보다 **한 단계 밝은** 자두색이 된다. 바깥 그림자는 검게, 안쪽 밝은 그림자는 아주 옅게(8%) 바꾼다. 흰 하이라이트를 라이트와 같은 세기로 두면 덩어리가 플라스틱처럼 번들거린다.

```css
:root[data-theme="dark"] {
  --bg:           #1F1A2E;
  --bg-peach:     #3B2A2A;
  --bg-mint:      #1F3530;
  --surface:      #2C2541;
  --surface-2:    #251F37;
  --text:         #F1EDFB;
  --muted:        #B9B0D3;  /* surface 대비 7.4:1 */
  --border:       #3D3557;
  --primary:      #9C86F2;  /* 다크에서는 밝힌다, 글자는 진한 색 */
  --primary-deep: #7F66E6;
  --accent:       #F5A07A;
  --accent-text:  #FFC7AC;
  --mint-text:    #9BE3C2;
  --danger:       #FF8A9A;
  --btn-text:     #1F1A2E;
}
```

### 역할 규칙

| 토큰 | 쓰는 곳 | 쓰지 않는 곳 |
|---|---|---|
| `--primary` | 주 버튼 1개, 선택된 탭 표시, 체크박스 체크 | 카드 배경, 제목 글자 |
| `--accent` | 진행률 채움, 오늘 일정 1건 표시 점 — **화면당 '지금' 1~2곳** | 버튼 배경, 큰 면 |
| `--bg-peach` / `--bg-mint` | 각각 화면당 카드 1개씩 바탕 | 페이지 전체 바탕 |
| `--muted` | 라벨, 시간, 보조 설명 | 파스텔 면(`--bg-peach`·`--bg-mint`) 위 글자 → 그 면의 `*-text` 사용 |

파스텔 면 위 글자는 반드시 같은 계열의 진한 색(`--accent-text`, `--mint-text`)을 쓴다. 회색(`--muted`)을 피치·민트 위에 올리면 대비가 4:1 아래로 떨어진다.

---

## 3. 타이포그래피

| 역할 | 서체 | 불러오기 |
|---|---|---|
| 한글 본문·UI | **Pretendard Variable** | `cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css` |
| 큰 제목(22px 이상)만 | **Jua** (둥근 한글) | Google Fonts `family=Jua` |
| 숫자·영문 포인트 | **Fredoka** 500~600 | Google Fonts `family=Fredoka:wght@500;600` |

```css
--font-sans:    "Pretendard Variable", Pretendard, -apple-system, "Malgun Gothic", sans-serif;
--font-display: "Jua", "Pretendard Variable", sans-serif;
--font-num:     "Fredoka", "Pretendard Variable", sans-serif;
.num { font-family: var(--font-num); font-variant-numeric: tabular-nums; }
```

Jua는 굵기가 하나뿐이고 글자가 귀엽다. **화면당 제목 2~3곳**에만 쓴다. 본문·버튼·목록까지 Jua로 쓰면 아이 학습지처럼 보인다.

### 스케일 (6단계)

| 토큰 | 크기 / 굵기 | 서체 | 용도 |
|---|---|---|---|
| `display` | 34px / 400 (Jua) | display | 페이지 인사말 1줄 |
| `h2` | 22px / 400 (Jua) | display | 카드 큰 제목 |
| `metric` | 40px / 600 | num | 진행률 %, D-day |
| `h3` | 16px / 700 | sans | 목록 항목 제목 |
| `body` | 14px / 500, 행간 1.55 | sans | 설명 |
| `label` | 12px / 600, `--muted` | sans | 라벨, 시간 |

본문 굵기는 400이 아니라 **500**으로 둔다. 파스텔 위에서 400은 가늘어 보여 흐리게 읽힌다.

---

## 4. 컴포넌트

### 카드 (덩어리)

```css
.clay {
  background: var(--surface);
  border-radius: 28px;
  padding: 24px;
  box-shadow: var(--clay-lg);
}
.clay--peach { background: var(--bg-peach); }
.clay--mint  { background: var(--bg-mint); }
.clay-inner {                 /* 카드 안 블록: 동심 반경 28 - 8 = 20 */
  background: var(--surface-2);
  border-radius: 20px;
  padding: 14px 16px;
  box-shadow: inset 3px 3px 6px rgba(106, 79, 214, .10),
              inset -3px -3px 6px rgba(255, 255, 255, .85);
}
```

카드 안 블록은 **움푹 들어간(inset)** 처리, 카드 자체는 볼록 처리. 볼록 안에 볼록을 또 넣지 않는다.

### 버튼

```css
.btn {
  height: 48px; padding: 0 22px;
  border: 0; border-radius: 18px;
  font: 700 15px/1 var(--font-sans);
  cursor: pointer;
  transition: transform 160ms ease, box-shadow 160ms ease;
}
.btn-primary {
  color: var(--btn-text);
  background: linear-gradient(180deg, var(--primary) 0%, var(--primary-deep) 100%);
  box-shadow: 8px 10px 20px rgba(86, 57, 194, .32),
              inset 3px 3px 6px rgba(255, 255, 255, .35),
              inset -4px -4px 8px rgba(40, 20, 120, .30);
}
.btn-soft {                    /* 보조 버튼 — 카드와 같은 덩어리 */
  color: var(--text);
  background: var(--surface);
  box-shadow: var(--clay-md);
}
.btn:hover  { transform: translateY(-1px); }
.btn:active { transform: translateY(1px); box-shadow: var(--clay-pressed); }
.btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
```

주 버튼은 화면에 1개. 나머지는 `.btn-soft`.

### 배지 · 칩

```css
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  height: 28px; padding: 0 12px; border-radius: 999px;
  font: 600 12px/1 var(--font-sans);
  background: var(--surface-2); color: var(--muted);
  box-shadow: inset 2px 2px 4px rgba(106, 79, 214, .10),
              inset -2px -2px 4px rgba(255, 255, 255, .9);
}
.badge--done { background: var(--bg-mint);  color: var(--mint-text); }
.badge--today{ background: var(--bg-peach); color: var(--accent-text); }
```

### 입력

```css
.field {
  height: 48px; width: 100%; padding: 0 18px;
  border: 1.5px solid transparent; border-radius: 18px;
  background: var(--surface-2); color: var(--text);
  font: 500 15px var(--font-sans);
  box-shadow: inset 4px 4px 8px rgba(106, 79, 214, .12),
              inset -4px -4px 8px rgba(255, 255, 255, .9);
}
.field::placeholder { color: var(--muted); }
.field:focus { outline: none; border-color: var(--primary); }
.field[aria-invalid="true"] { border-color: var(--danger); }
.field-error { font: 600 12px var(--font-sans); color: var(--danger); margin-top: 6px; }
```

### 체크리스트 항목

```css
.check {
  width: 28px; height: 28px; border-radius: 10px; flex: none;
  background: var(--surface-2);
  box-shadow: inset 2px 2px 5px rgba(106, 79, 214, .16), inset -2px -2px 5px #fff;
  display: grid; place-items: center;
}
.check[aria-checked="true"] {
  background: var(--primary);
  box-shadow: 3px 4px 8px rgba(86, 57, 194, .30), inset 2px 2px 4px rgba(255,255,255,.35);
}
.todo.done .todo-title { color: var(--muted); text-decoration: line-through; }
```

### 내비게이션

사이드 내비는 하나의 큰 클레이 덩어리 안에 항목을 쌓고, 선택된 항목만 `--clay-md`로 한 번 더 떠오르게 한다. 상단 내비는 알약형 세그먼트(트랙 = inset, 선택 = 볼록)로.

```css
.nav { background: var(--surface); border-radius: 28px; padding: 12px; box-shadow: var(--clay-lg); }
.nav a { display: flex; align-items: center; gap: 12px; height: 46px; padding: 0 14px;
         border-radius: 18px; color: var(--muted); font-weight: 600; }
.nav a[aria-current="page"] { background: var(--surface); color: var(--text); box-shadow: var(--clay-md); }
```

---

## 5. 효과 레시피

이 스타일의 효과는 **클레이 그림자 하나**다. 그림자는 아래 3개 토큰 외 값을 만들지 않는다.

```css
:root {
  /* 큰 덩어리: 카드, 내비 */
  --clay-lg:
    16px 18px 36px rgba(106, 79, 214, .20),   /* 바깥 아래쪽 그림자 */
    -10px -10px 28px rgba(255, 255, 255, .80), /* 바깥 위쪽 빛 */
    inset 6px 6px 12px rgba(255, 255, 255, .85),  /* 안쪽 밝은 볼록 */
    inset -8px -8px 16px rgba(106, 79, 214, .12); /* 안쪽 어두운 볼록 */

  /* 작은 덩어리: 보조 버튼, 선택된 탭, 아바타 */
  --clay-md:
    8px 10px 20px rgba(106, 79, 214, .20),
    -6px -6px 14px rgba(255, 255, 255, .80),
    inset 3px 3px 6px rgba(255, 255, 255, .85),
    inset -4px -4px 8px rgba(106, 79, 214, .12);

  /* 눌림: 버튼 :active, 토글 ON 트랙 */
  --clay-pressed:
    inset 5px 5px 10px rgba(106, 79, 214, .22),
    inset -5px -5px 10px rgba(255, 255, 255, .70);
}
:root[data-theme="dark"] {
  --clay-lg:
    16px 18px 36px rgba(0, 0, 0, .45),
    -8px -8px 24px rgba(255, 255, 255, .04),
    inset 6px 6px 12px rgba(255, 255, 255, .08),
    inset -8px -8px 16px rgba(0, 0, 0, .30);
  --clay-md:
    8px 10px 20px rgba(0, 0, 0, .40),
    inset 3px 3px 6px rgba(255, 255, 255, .08),
    inset -4px -4px 8px rgba(0, 0, 0, .30);
  --clay-pressed:
    inset 5px 5px 10px rgba(0, 0, 0, .45),
    inset -4px -4px 8px rgba(255, 255, 255, .05);
}
```

**그림자 색은 회색이 아니라 라벤더 계열(`rgba(106,79,214,…)`)이다.** 회색 그림자를 쓰면 파스텔 위에서 지저분한 얼룩처럼 보인다.

**쓰는 양의 상한**
- `--clay-lg`: 한 화면에 덩어리 **8개 이하**. 그 이상이면 카드 안 블록을 inset 처리로 바꿔 덩어리 수를 줄인다
- 그라디언트: **주 버튼 1종**(위→아래, 같은 색상의 명도 차이)만. 카드·배경 그라디언트 금지
- 배경 장식: 페이지 가장자리 파스텔 원 **2개 이하**(단색, 블러 없음, 화면 밖으로 반쯤 걸치게)
- 클레이 위에 유리(blur)·네온·글로우를 겹치지 않는다

---

## 6. 레이아웃

| 토큰 | 값 |
|---|---|
| `--gap` | 24px (덩어리 사이 — 그림자가 퍼질 자리가 필요해 일반 UI보다 넓다) |
| `--pad` | 24px (카드), 모바일 18px |
| 반경 | 28 / 20 / 18 / 999 |
| 최대 폭 | 1240px, 좌우 여백 28px |

데스크톱은 **좌측 내비(220px) + 본문 12열**. 본문은 비대칭으로 짠다. 예: 인사 + 진행률(8열) / 멘토 카드(4열), 체크리스트(7열, 세로로 긴) / 오늘 일정(5열). 같은 크기 카드 3개를 나란히 두지 않는다.

```css
.shell { display: grid; grid-template-columns: 220px 1fr; gap: 24px; max-width: 1240px; margin: 0 auto; padding: 28px; }
.board { display: grid; grid-template-columns: repeat(12, minmax(0,1fr)); gap: 24px; align-items: start; }
```

그림자가 잘리지 않도록 **덩어리의 부모에는 `overflow: hidden`을 주지 않는다.** 스크롤 영역이 필요하면 안쪽 패딩을 그림자 반경(36px)만큼 준다.

모바일(≤640px)에서는 1열, 내비는 하단 탭(알약형 덩어리 하나)으로 옮기고 그림자 크기를 `--clay-md` 수준으로 낮춘다.

---

## 7. 모션

| 상황 | 처리 | 시간 |
|---|---|---|
| 버튼 누름 | `translateY(1px)` + `--clay-pressed` | 160ms ease |
| 체크 완료 | 체크박스 `scale(.92 → 1.06 → 1)` | 280ms `cubic-bezier(.34,1.56,.64,1)` |
| 진행률 증가 | 채움 폭 변화 | 400ms ease-out |
| 카드 등장 | 8px 아래에서 위로 + 페이드, 40ms 순차 | 320ms |

말랑한 탄성(overshoot)은 **체크 완료 한 곳**에만 쓴다. 모든 호버에 튕기는 모션을 넣으면 산만하다.

```css
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
```

---

## 8. 접근성

파스텔 스타일은 **글자 대비가 가장 먼저 무너진다.** 아래를 기본값으로 둔다.

1. **본문은 자두색 `--text`(#2A2342)**. 바탕·카드 어디서나 11:1 이상
2. **보조 글자 `--muted`(#5B5276)는 라벤더 계열 바탕(`--bg`, `--surface`, `--surface-2`)에만.** 피치·민트 면 위에서는 `--accent-text` / `--mint-text`로 바꾼다
3. **흰 글자는 `--primary`(#6A4FD6) 이상 진한 면에만.** 피치(#F08A5D) 위 흰 글자는 2.6:1이라 금지 → 피치 면에는 진한 글자
4. 그림자만으로 경계를 만들면 저시력 사용자에게 카드가 사라진다. 바탕과 카드의 **명도 차이(#ECE6FB → #F8F5FF)**를 유지하고, 입력란은 포커스 시 1.5px 테두리를 반드시 보인다
5. 완료·오늘·지연 상태를 색으로만 구분하지 않는다. 배지에 글자("완료", "오늘", "3일 지남") + 아이콘
6. 포커스 링 3px `--accent`, offset 3px — 클레이 그림자에 묻히지 않게 바깥으로 띄운다
7. 터치 타깃 44px 이상(버튼 48px, 체크박스는 행 전체를 클릭 영역으로)
8. Jua는 22px 미만에 쓰지 않는다 (작으면 획이 뭉쳐 읽기 어렵다)

대비 점검: 새 파스텔을 추가하면 그 면 위 글자 색을 **먼저** 정하고 4.5:1을 확인한 뒤에 쓴다.

---

## 9. 하지 말 것

- 회색 그림자(`rgba(0,0,0,.1)`) — 파스텔 위에서 때 묻은 것처럼 보인다
- 카드 안 블록까지 볼록 그림자 → 덩어리가 덩어리를 품어 깊이가 뒤죽박죽
- 반경을 4종 이상 섞기, 혹은 8px 같은 작은 반경
- 피치·민트 위 회색 글자, 피치 위 흰 글자
- 페이지 배경 전체 그라디언트, 블롭이 떠다니는 배경, 클레이 + 유리 혼합
- 3D 일러스트 이미지를 구해 붙이기(외부 이미지) — 필요한 그래픽은 CSS 덩어리나 인라인 SVG로
- 모든 텍스트를 Jua로
- 아이콘+제목+설명 카드 3개를 같은 크기로 나열
- 이모지 (축하·완료 표현도 아이콘 + 문장으로)
- 체크리스트만 있고 "아직 아무것도 없음", "불러오는 중", "저장 실패" 상태가 없는 화면

---

## 10. 셀프 체크리스트

- [ ] 그림자 값이 `--clay-lg` / `--clay-md` / `--clay-pressed` 3개뿐인가?
- [ ] 그림자 색이 라벤더 계열인가? 회색이 섞이지 않았나?
- [ ] 한 화면 볼록 덩어리가 8개 이하인가?
- [ ] 반경이 28 / 20 / 18 / 999 외에 없나?
- [ ] 피치·민트 면 위 글자가 같은 계열 진한 색인가?
- [ ] 흰 글자가 `--primary`보다 연한 면 위에 있지 않나?
- [ ] 주 버튼이 1개인가? 눌렀을 때 실제로 들어가나?
- [ ] Jua가 제목 2~3곳에만 쓰였나?
- [ ] 빈 상태·로딩(덩어리 모양 스켈레톤)·에러 문구가 있나?
- [ ] 이름·날짜·팀명이 현실적인가? (예: "품질보증팀 김서윤 책임", "2026.03.04(수) 10:00")
- [ ] `prefers-reduced-motion`에서 모든 모션이 멈추나?

### 빠른 토큰

```
bg #ECE6FB · surface #F8F5FF · surface-2 #EFE9FD · peach #FFE3D3 · mint #D3F2E4
text #2A2342 · muted #5B5276 · border #DCD3F4 · primary #6A4FD6 · accent #F08A5D
radius 28 / 20 / 18 · gap 24 · Pretendard + Jua(제목) + Fredoka(숫자)
shadow: 바깥 라벤더 + 바깥 흰 빛 + 안쪽 흰 볼록 + 안쪽 라벤더 볼록 (4겹)
```

### 복사용 프롬프트

```
클레이모피즘 스타일로 신입사원 온보딩 화면을 만들어줘. designs/claymorphism.md를 따른다.
- 바탕 라벤더 #ECE6FB, 카드 #F8F5FF, 피치 #FFE3D3·민트 #D3F2E4 면은 각 1개씩만
- 카드 그림자는 문서 5장의 --clay-lg 4겹 레시피 그대로 (그림자 색은 라벤더 rgba(106,79,214,…))
- 반경 카드 28px / 내부 20px / 버튼 18px만, 카드 안 블록은 inset(움푹) 처리
- 구성: 인사 + 첫 주 진행률(큰 숫자 Fredoka), 첫 주 할 일 체크리스트, 담당 멘토 카드, 오늘 일정
- 같은 크기 카드 3개 나열 금지, 12열 비대칭 배치
- 글자는 자두색 #2A2342, 피치·민트 위에는 같은 계열 진한 글자. 흰 글자는 #6A4FD6 버튼에만
- 주 버튼 1개, 눌림 상태(--clay-pressed) 포함. 빈 상태·로딩·에러 포함, 이모지 금지
```
