Feedback
피드백은 사용자의 입력에 대한 즉각적인 반응이에요. 요소가 조작 가능하다는 사실과 입력이 전달됐다는 사실을 알려줘요.
사용자가 요소를 누르면 인터페이스는 그 입력이 전달됐음을 즉시 알려야 해요. 이 즉각적인 반응이 피드백이에요. State 가 정의한 pressed 상태를 사용자가 눈과 손으로 알아챌 수 있게 드러내는 역할을 해요.
Feedback Elements
| 피드백 요소 | 전달 방식 |
|---|---|
| Color | 눌린 동안 표면 색이 바뀌어요 |
| Scale | 눌린 동안 요소가 정해진 거리만큼 작아져요 |
| Haptic | 누른 순간 손끝에 진동이 전해져요 |
이 세 가지를 함께 사용해 눌림을 전달해요. 색은 누를 수 있는 요소 전반에 적용되는 기본 요소이고, 축소는 요소의 구조가 허락할 때 여기에 더해져요. Haptic은 팀에서 필요에 따라 정의해 사용해요.
Principles
피드백 요소를 함께 사용하기
한 번의 입력에서 여러 피드백 요소가 동작하면, 사용자는 이를 하나의 반응으로 인지해야 해요. 시작 시점이나 속도가 서로 다르면 반응이 둘로 나뉘어 읽히고, 요소가 잘못 동작하는 것처럼 느껴져요. 그래서 색 전환($duration.color-transition)과 축소($duration.pressed-scale)는 같은 $duration.d3(150ms)를 참조해요.
어떤 환경에서도 반응 남기기
축소는 OS의 동작 줄이기 설정이 켜져 있으면 일어나지 않고, Haptic은 기기에 따라 전달되지 않아요. 어떤 요소가 빠지더라도 눌렸다는 사실은 남아야 하므로, 설정의 영향을 받지 않는 색 전환을 기본으로 두고 나머지를 그 위에 더해요.
Color
색은 눌림을 알리는 가장 기본적인 방법이에요. 표면 색이 바뀌는 것만으로 사용자는 자신이 어디를 눌렀는지, 입력이 요소에 도달했는지 바로 알 수 있어요.
눌린 색은 토큰으로 정의해요
눌린 색은 새로 만들지 않고, 역할 기반 색상 체계 안에서 -pressed 가 붙은 짝 토큰을 사용해요. Action Button의 brandSolid는 기본 상태에서 $color.bg.brand-solid 를, 눌린 동안 $color.bg.brand-solid-pressed 를 써요.
| 기본 | 눌림 (pressed) | Light | Dark |
|---|---|---|---|
색은 표면에서만 바뀌어요
색 전환은 요소의 표면에서 일어나요. 그 위에 놓인 텍스트와 아이콘의 색은 눌림에 반응하지 않아요. 표면과 글자 색이 함께 바뀌면 요소 전체가 다른 요소로 바뀐 것처럼 읽혀요. 그래서 전경용 pressed 토큰은 제공하지 않아요.
직접 만드는 요소에 적용할 때
- 기본 색에 대응하는 pressed 토큰 찾기 — 배경이
$color.bg.neutral-weak라면 눌린 동안$color.bg.neutral-weak-pressed를 사용해요. - 기본 상태에 배경색이 없는 경우 — ghost 버튼처럼 평소 배경이 없는 요소도 눌린 동안에는
$color.bg.transparent-pressed표면이 생겨요. 알파 값이라 어떤 배경 위에서도 자연스럽게 겹쳐요. floating 레이어 위에서는$color.bg.layer-floating-pressed를 사용해요. - 색을 쓰지 않는 경우인지 확인하기 — Switch나 탭처럼 색이 이미 상태(켜짐/선택)를 나타내는 요소는 눌림에 색을 쓰지 않고 축소만 사용해요. 아직 손을 떼지 않았는데 상태가 바뀐 것처럼 보이기 때문이에요.
Scale
누를 수 있는 요소는 눌린 동안 살짝 작아져요. 물리적인 버튼이 눌려 들어가는 감각을 빌려와, 손끝의 입력이 요소에 도달했음을 바로 알게 해요.
중요한 것은 모든 요소에서 눌린 느낌의 양이 같게 느껴지는 것이에요. 고정 배율(예: 0.98)은 요소가 클수록 실제로 움직이는 거리가 커져요.
| 요소 크기 (폭 × 높이) | 배율 | 가로 축소 | 세로 축소 |
|---|---|---|---|
| 80 × 52 | 0.98 | 1.6px | 1.04px |
| 160 × 52 | 0.98 | 3.2px | 1.04px |
| 343 × 52 (모바일 전체 폭) | 0.98 | 6.9px | 1.04px |
| 720 × 52 (태블릿 전체 폭) | 0.98 | 14.4px | 1.04px |
그래서 눌렸을 때 요소가 정해진 거리만큼 줄어든다고 정의해요. 배율은 요소의 실제 크기에서 계산되는 결과예요.
계산 방식
basis = max(요소 높이, 요소 폭 ÷ 4, 24)
배율 = (basis - 2) ÷ basis- 기준 길이(basis)는 기본적으로 요소의 높이예요.
- 요소 폭 ÷ 4 — 좌우로 긴 요소가 가로로 지나치게 많이 움직이지 않도록 보정해요.
- 최소 24 — 아주 작은 요소의 축소 비율이 8.3%를 넘지 않도록 보장해요.
폭과 높이를 바꿔 보며 배율을 확인하고, 버튼을 눌러 보세요.
- basis
- 48.0 (높이)
- 배율
- 0.958
- 가로 축소
- 5.0px
- 세로 축소
- 2.0px
Clay 컴포넌트의 축소
registry 컴포넌트는 Tailwind 유틸로 기본 크기에 맞춘 근사 배율을 사용해요. 일반 요소는 enabled:active:scale-[0.97], 작은 요소는 0.95 를 써요.
축소 규칙
- 축소는 항상 요소의 정가운데를 기준으로 일어나요.
- 축소는 자리를 바꾸지 않아요. 눌린 요소가 작아져도 주변 요소는 움직이지 않아요.
- 문장 안의 링크 텍스트처럼 영역이 하나의 면으로 잡히지 않는 요소에는 적용하지 않아요.
- 카드 안에 열기·좋아요·저장처럼 대등한 동작이 나란히 있으면 카드 전체가 아닌 눌린 타깃만 반응해요.
- List Item처럼 배경이 화면 폭을 채우는 구조는 배경은 그대로 두고 콘텐츠만 줄여요(Content Scale).
- 이미 축소하는 요소 안에 놓인 요소는 자신의 축소를 꺼요. 겹치면 배율이 곱해져 과도하게 작아져요.
다른 상태와 함께 있을 때
- Selected — 누르면 축소돼요. 선택 색상은 그대로 유지돼요.
- Loading — 축소하지 않아요. 이미 진행 중인 동작이 있어 추가 입력을 받지 않아요.
- Disabled — 축소하지 않아요.
Accessibility
OS의 동작 줄이기(Reduce Motion) 설정이 켜져 있으면 배율은 항상 1 이 되어 축소가 일어나지 않아요. 이때는 색 변화가 눌림을 전달해요.