Communitygithub.com

ZainR525/PersonalWebsiteZainRizvi

Apple의 인터페이스 디자인과 유동적이고 물리적인 모션을 웹 환경에 맞게 변환한 프롬프트입니다. 제스처 기반 UI, 스프링 애니메이션, 드래그/스와이프/시트 상호작용, 모멘텀 및 중단 가능한 전환, 반투명 소재와 깊이, 타이포그래피(광학 크기, 자간, 행간), 축소 모션, 또는 Apple 스타일 인터페이스의 디자인 기초(피드백, 공간 일관성, 절제)를 구축하거나 검토할 때 사용합니다.

PersonalWebsiteZainRizvi란 무엇인가요?

이 스킬은 Apple의 WWDC 디자인 강연(특히 2018년 'Designing Fluid Interfaces')에서 발췌한 핵심 원칙들을 웹 플랫폼(CSS, Pointer Events, requestAnimationFrame, Motion/Framer Motion과 같은 스프링 라이브러리)에 맞게 해석하고 적용하는 방법을 제공합니다. 핵심 아이디어는 인터페이스가 물리적 세계처럼 행동해야 한다는 것입니다. 즉, 즉각적으로 반응하고, 연속적으로 움직이며, 모멘텀을 전달하고, 경계에서 저항하며, 움직임 중간에 방향을 전환할 수 있어야 합니다. 이 스킬은 응답성(지연 시간 제거), 직접 조작(1:1 추적), 중단 가능성(모든 애니메이션이 현재 값에서 시작하고 속도를 상속받음), 스프링 애니메이션(고정된 지속 시간 대신 감쇠와 응답 사용), 속도 핸드오프(드래그에서 애니메이션으로의 매끄러운 전환), 모멘텀 투영(속도 기반 목표 지점 예측), 공간 일관성, 고무줄 효과(부드러운 경계), 제스처 디자인 세부 사항, 프레임 수준의 부드러움, 재료와 깊이(반투명 레이어로 계층 구조 전달), 다중 감각 피드백 등 Apple 스타일 인터페이스의 핵심 원칙을 망라합니다. 웹 개발자, UI/UX 엔지니어, 프론트엔드 개발자, 디자인 시스템 구축자에게 적합합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ZainR525/PersonalWebsiteZainRizvi/tree/HEAD/.claude/skills/apple-design

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Apple Design

초기 응답

이 스킬이 특정 질문 없이 처음 호출되면, 다음 내용으로만 응답하세요:

Apple 스타일의 유동적인 인터페이스를 웹에서 구축할 수 있도록 도와드릴 준비가 되었습니다. 제 지식은 Apple의 WWDC 디자인 강연을 웹 환경에 맞게 변환한 내용에서 비롯되었습니다.

사용자가 질문하기 전까지는 다른 정보를 제공하지 마십시오.

Apple이 컴퓨터처럼 느껴지지 않고 당신의 연장처럼 느껴지는 인터페이스를 구축하는 방법입니다. 이 지식은 Apple의 WWDC 디자인 강연 — 주로 Designing Fluid Interfaces (WWDC 2018) — 에서 추출하여 웹 플랫폼(CSS, Pointer Events, requestAnimationFrame, Motion/Framer Motion과 같은 스프링 라이브러리)에 맞게 정제한 것입니다.

핵심 요지는 다음과 같습니다: 모션이 현재 화면상의 값에서 시작하고, 사용자의 속도를 상속받으며, 모멘텀을 앞으로 투영하고, 어떤 순간에도 잡아서 방향을 바꿀 수 있을 때 인터페이스는 살아있는 듯이 느껴집니다. 스프링은 이러한 모든 것을 자연스럽게 만드는 도구입니다. 본질적으로 중단 가능하고 속도를 인식하기 때문입니다.

핵심 아이디어

"인터페이스를 우리가 생각하고 움직이는 방식에 맞출 때, 놀라운 일이 일어납니다. 컴퓨터처럼 느껴지지 않고 우리의 매끄러운 연장처럼 느껴지기 시작합니다."

인터페이스가 물리적 세계처럼 행동할 때 유동적입니다. 즉시 반응하고, 연속적으로 움직이고, 모멘텀을 전달하고, 경계에서 저항하며, 움직임 중간에 방향을 바꿀 수 있습니다. 아래의 모든 내용은 이 목표에 더 가까워지기 위한 방법입니다.

Apple은 디자인이 네 가지 인간 욕구를 충족시키는 것으로 봅니다: 안전/예측 가능성, 이해, 성취, 즐거움. 여기의 모든 규칙은 이 중 하나를 위한 것입니다.

1. 응답 — 지연 시간 제거

지연이 나타나는 순간, 직접적인 느낌은 "절벽에서 떨어집니다." 응답은 다른 모든 것이 구축되는 기초입니다.

  • 눌렀을 때가 아니라, 포인터를 내렸을 때 반응하세요. 버튼을 누르는 순간 강조하세요. click/터치 업을 기다렸다가 피드백을 보여주면 죽은 듯한 느낌을 줍니다.
  • 모든 지연에 주의하세요. 디바운스, 인공 타이머, 전환 대기, 그리고 약 300ms의 탭 지연을 감사하세요. 입력 경로에 필수적이지 않은 모든 것은 퇴보입니다.
  • 피드백은 상호작용 중에 연속적이어야 하며, 끝에서만 제공되어서는 안 됩니다. 드래그, 슬라이더, 서랍의 경우, 제스처가 완료될 때만 애니메이션을 적용하지 말고 전체 과정 동안 포인터와 1:1로 UI를 업데이트하세요.
/* 피드백은 누를 때 있으며, 즉각적입니다 */
.button:active {
  transform: scale(0.97);
  transition: transform 100ms ease-out;
}

2. 직접 조작 — 1:1 추적

"터치와 콘텐츠는 함께 움직여야 합니다."

사용자가 무언가를 드래그할 때, 반드시 손가락에 붙어 있어야 하며 — 잡은 위치의 오프셋을 존중해야 합니다. 잡을 때 요소의 중앙으로 스냅하면 즉시 환상이 깨집니다.

  • Pointer Events와 setPointerCapture를 사용하여 포인터가 요소의 경계를 벗어나도 추적이 계속되도록 하세요.
  • 짧은 속도/위치 기록 (마지막 몇 개의 pointermove 이벤트)을 현재 지점뿐만 아니라 추적하세요 — 해제 시 속도가 필요합니다.
el.addEventListener('pointerdown', (e) => {
  el.setPointerCapture(e.pointerId);
  const grabOffset = e.clientY - el.getBoundingClientRect().top; // 잡은 위치 존중
  // ...속도 및 타임스탬프 기록 추적
});

3. 중단 가능성 — 가장 중요한 단일 원칙

"생각과 제스처는 동시에 일어납니다."

모든 애니메이션은 중단 가능하고 언제든지 방향을 전환할 수 있어야 합니다. 사용자는 움직이는 요소를 공중에서 잡아서 애니메이션이 끝날 때까지 기다리지 않고 방향을 바꿀 수 있어야 합니다. 닫히는 모달을 사용자가 다시 잡으면 손가락을 따라야 합니다 — 먼저 닫히고 다시 열리지 않아야 합니다.

  • 전환 중에는 절대 입력을 잠그지 마세요.
  • 항상 표현(현재) 값에서 애니메이션을 시작하고, 목표 값에서 시작하지 마세요. 중단 시, 요소의 실시간 화면 변환 값을 읽고 거기서 새 애니메이션을 시작하세요. 논리적/목표 값에서 시작하면 눈에 띄는 점프가 발생합니다.
  • 제스처 기반 상호작용에는 CSS 전환과 @keyframes를 피하세요 — 부드럽게 잡아서 중간에 방향을 바꿀 수 없습니다. 스프링은 기본적으로 현재 값에서 애니메이션을 시작하는데, 이는 중단에 정확히 필요한 동작입니다.
  • 제스처가 반전될 때, 속도를 급격히 자르지 말고 혼합하세요. 반전 지점에서 한 애니메이션을 다른 애니메이션으로 대체하면 속도 불연속성, 즉 "벽돌 벽"이 생성됩니다. 재타겟팅을 통해 속도를 유지하는 스프링 라이브러리는 이를 피합니다. (이것이 iOS의 가산 애니메이션이 기본적으로 하는 일입니다. 웹에서는 현재 속도에서 재타겟팅하는 스프링 라이브러리를 선택하세요.)
  • 2D 모션을 독립적인 X, Y 스프링으로 분해하세요. 2D 거리에 단일 스프링을 사용하면 X와 Y의 속도가 다를 때 동기화가 깨집니다.

4. 애니메이션보다 행동 — 스프링 사용

"애니메이션을 인터페이스가 규정한 것이 아니라, 당신과 객체 간의 대화로 생각하세요."

미리 정해진 고정된 지속 시간의 애니메이션은 새로운 입력에 반응할 수 없습니다. 스프링은 가능합니다 — 새로운 입력은 단지 목표를 바꾸고, 모션은 연속성을 유지합니다. 사용자가 터치할 수 있는 모든 것에는 스프링을 사용하세요.

Apple은 의도적으로 물리학 삼중항(질량/강성/감쇠)을 디자이너 친화적인 두 개의 매개변수로 대체했습니다. 다음 관점에서 생각하세요:

  • 감쇠 비율(Damping ratio) — 오버슈트를 제어합니다. 1.0 = 임계 감쇠, 바운스 없음, 부드러운 안정. < 1.0 = 오버슈트 및 진동. 낮을수록 더 바운스.
  • 응답(Response) — 값이 목표에 도달하는 속도(초). 낮을수록 더 빠릅니다. 이것은 "지속 시간"이 아닙니다 — 스프링에는 고정된 지속 시간이 없습니다. 안정 시간은 매개변수에서 발생합니다.

기본값:

  • 대부분의 UI는 감쇠 1.0 (임계 감쇠)으로 시작하세요 — 우아하고 방해가 되지 않습니다.
  • 제스처 자체가 모멘텀을 전달한 경우에만 (튕기기, 던지기, 드래그 해제) 바운스(감쇠 ~0.8)를 추가하세요. 방금 페이드 인된 메뉴의 오버슈트는 어색하게 느껴집니다. 하지만 당신이 튕긴 카드의 오버슈트는 자연스럽게 느껴집니다.

Apple이 출시한 구체적인 값:

상호작용감쇠응답
이동 / 재배치 (예: PiP)1.00.4
회전0.80.4
서랍 / 시트0.80.3

웹 매핑 (Motion / Framer Motion): bounce + duration 스프링 API는 Apple의 감쇠 + 응답과 밀접하게 매핑됩니다. 안전한 기본 스타일은 기본적으로 모든 곳에서 damping: 1.0 스프링을 사용하는 것입니다. 바운스는 모멘텀 기반의 물리적 상호작용을 위해 남겨두세요.

import { animate } from 'motion';

// 임계 감쇠 기본값 (오버슈트 없음)
animate(el, { y: 0 }, { type: 'spring', bounce: 0, duration: 0.4 });

// 모멘텀 상호작용 — 약간의 바운스, 튕기기가 선행되었기 때문
animate(el, { y: target }, { type: 'spring', bounce: 0.2, duration: 0.4 });

5. 속도 핸드오프 — 드래그와 애니메이션 사이의 이음새

제스처가 끝날 때, 애니메이션은 손가락의 정확한 속도로 계속되어야 드래그와 애니메이션 사이에 눈에 띄는 이음새가 없습니다. 이것이 "유동적"과 "괜찮음"을 가장 많이 구분짓는 세부 사항입니다.

포인터의 해제 속도를 스프링의 초기 속도로 전달하세요. 일부 스프링 API는 상대 속도를 필요로 합니다 — 목표까지의 남은 거리로 정규화하세요:

relativeVelocity = gestureVelocity / (targetValue − currentValue)

예시: 요소가 y=50, 목표 y=150 (100px 남음), 손가락이 50px/s로 움직임 → 초기 스프링 속도 = 50 / 100 = 0.5. Framer Motion / Motion은 절대 px/s 속도를 직접 받아들이므로(velocity 옵션), 일반적으로 원시 값을 전달합니다.

6. 모멘텀 투영 — 제스처가 향하는 곳으로 애니메이션

"작은 입력을 받아 큰 출력을 만드세요."

해제 지점에서 가장 가까운 경계로 스냅하지 마세요. 속도를 사용하여 정지 위치를 투영하세요 — 스크롤 감속과 정확히 동일하게 — 그런 다음 투영된 지점에 가장 가까운 목표로 스냅하세요. 이것이 튕기기가 요소를 던지는 것처럼 느껴지게 만드는 것입니다.

Apple의 정확한 투영 함수 (Designing Fluid Interfaces 샘플 코드에서):

// decelerationRate ≈ 0.998 (일반 스크롤 느낌), 0.99 (더 빠른 느낌)
function project(initialVelocity /* px/s */, decelerationRate = 0.998) {
  return (initialVelocity / 1000) * decelerationRate / (1 - decelerationRate);
}

const projectedEndpoint = currentPosition + project(releaseVelocity);
const target = nearestSnapPoint(projectedEndpoint);   // 투영에서 목표 선택
animateSpringTo(target, { velocity: releaseVelocity }); // 그런 다음 속도 전달 (§5)

참고: 물리학 교과서의 v²/(2·decel)은 Apple이 출시하는 형태가 아닙니다 — 위의 지수 감쇠 형태를 사용하세요. 이것은 좋은 바텀 시트와 캐러셀(Vaul, Embla)의 표준 동작입니다.

7. 공간 일관성 — 대칭 경로, 고정된 원점

"무언가가 한 방향으로 사라지면, 우리는 그것이 왔던 곳에서 나타날 것으로 기대합니다."

  • 같은 경로를 따라 들어오고 나가세요. 오른쪽에서 슬라이드 인 되는 패널은 오른쪽으로 사라져야 합니다. 오른쪽에서 들어와서 아래로 나가면 연결이 끊기고 혼란스럽습니다.
  • 상호작용을 그 근원에 고정하세요. 메뉴, 팝오버 또는 시트는 그것을 트리거한 요소에서 시작해야 합니다 — transform-origin을 트리거로 설정하여 버튼과 콘텐츠 간의 공간적 관계를 명확히 하세요. (이것은 팝오버가 중앙이 아닌 트리거에서 크기가 조정되는 동일한 원점 인식 포인트입니다.)
  • 가역 전환에 이징을 미러링하여 나가는 경로가 돌아오는 경로와 일치하도록 하세요 (두 방향에 대해 역 3차 베지어 제어점 사용).

8. 제스처 방향을 암시

인간은 궤적에서 최종 상태를 예측합니다. 중간 프레임은 사물이 어디로 가는지 암시해야 합니다 — 제어 센터 모듈은 "손가락을 향해 위로 그리고 바깥으로 자랍니다." 중간 프레임이 결과를 가리키도록 하고, 맹목적으로 보간하지 마세요.

9. 고무줄 효과 — 부드러운 경계

경계에서, 딱 멈추는 대신 점진적으로 저항하세요. 딱 멈추는 것은 "얼어붙은" 것으로 읽히고, 지속적인 저항은 "반응하지만, 더 이상 할 게 없습니다"로 읽힙니다. 사용자가 경계를 넘어 드래그할수록 증가하는 감쇠를 적용하세요.

// 경계를 넘을수록 요소가 덜 따라옵니다 — 실제 사물은 멈추기 전에 느려집니다
function rubberband(overshoot, dimension, constant = 0.55) {
  return (overshoot * dimension * constant) / (dimension + constant * Math.abs(overshoot));
}

10. 제스처 디자인 세부 사항 ("느낌" 체크리스트)

  • 탭: 터치 다운 시 강조 (즉시), 터치 업 시 커밋. 대상 주위에 약 10px의 히스테리시스/히트 패딩을 추가하고, 드래그하여 취소 및 다시 돌아오기를 허용하세요.
  • 드래그/스와이프: 방향을 결정하기 전에 작은 움직임 임계값(히스테리시스, ~10px)을 요구한 다음 1:1로 추적하세요.
  • 첫 움직임부터 가능한 모든 제스처를 병렬로 감지하고, 의도가 명확해지면 패자들을 자신있게 취소하세요. 최종 상태만 보고하는 인식기(swipeleft-유형 이벤트)를 피하세요 — 피드백에 필요한 연속 추적을 버립니다.
  • 명확하지 않은 지연을 최소화하세요. 더블 탭 감지는 불가피하게 단일 탭을 지연시킵니다. 더블 탭이 실제로 존재하는 곳에서만 그 비용을 지불하세요.

11. 프레임 수준의 부드러움

부드러움은 프레임 속도뿐만 아니라 프레임 안에 있는 것에 관한 것입니다.

  • 프레임당 위치 변화를 인식 임계값 미만으로 유지하여 스트로빙을 피하세요.
  • 매우 빠른 모션의 경우, 미묘한 모션 블러/스트레치가 속도를 인코딩하며, 딱딱하고 날카로운 선보다 더 좋게 읽힙니다.
  • requestAnimationFrame는 웹의 디스플레이 동기화 클럭입니다(Apple은 CADisplayLink를 사용합니다). 컴포지터 친화적인 속성인 transform과 opacity만 애니메이션하고, 모션이 임박한 곳에 will-change를 힌트로 주세요.

12. 재료 및 깊이 — 반투명이 계층 구조를 전달

Apple은 반투명 재료를 주의를 빼앗지 않으면서 구조를 제공하는 떠다니는 기능적 레이어로 사용합니다. 웹에서는 backdrop-filter로 근사하세요.

  • 탐색/툴바/시트를 반투명 레이어로 구축하세요 (backdrop-filter: blur() + 반투명 배경) — 고정된 스트립을 소비하는 불투명 막대가 아닌, 콘텐츠가 아래에서 스크롤되도록 하세요.
  • 재료 무게는 계층 구조를 인코딩합니다: 더 어둡고/무거운 재료는 구조적 영역(사이드바)을 분리하고, 더 가벼운 재료는 상호작용 요소(버튼)에 주의를 끕니다. 가벼운 반투명 표면을 다른 표면 위에 절대 쌓지 마세요 — 가독성이 무너집니다.
  • 더 큰 표면은 더 두껍게 읽혀야 합니다: 작은 칩보다 더 강한 블러와 더 깊은 그림자. 컨텍스트 인식 그림자를 고려하세요 — 분리를 위해 바쁜/텍스트 콘텐츠 위에서는 더 무겁게, 평범한 배경 위에서는 더 가볍게.
  • 집중을 위해 어둡게 하고, 흐름을 유지하기 위해 분리하세요. 모달 작업은 표면을 어두워지는 스크림과 짝을 이루고 배경을 뒤로/아래로 밉니다. 병렬적이고 차단하지 않는 패널은 흐름이 끊어지지 않도록 스크림 없이 반투명도와 오프셋을 사용합니다. 쌓인 시트의 경우, 각 상위 레이어를 점진적으로 어둡게 하고 뒤로 밉니다.
  • 생생함(Vibrancy)은 변화하는 배경 위에서 텍스트 가독성을 유지합니다. 흐릿/반투명 표면 위에서는 평평한 회색 텍스트를 사용하지 마세요 — 더 높은 대비, 약간 더 무거운 두께, 작은 자간 증가를 사용하세요. 색상은 반투명 전경이 아닌 단색 레이어에 두세요.
  • 딱딱한 구분선 대신 스크롤 가장자리 효과. 고정 헤더 아래에 1px 테두리 대신, 떠 있는 크롬이 실제로 콘텐츠와 겹치는 곳에서만 콘텐츠가 떠 있는 크롬과 만나는 곳에 작은 블러/그라디언트 마스크를 페이드하세요.
  • 그냥 페이드하지 말고, 물질화하세요. 유리/블러 표면의 경우, 들어오고 나갈 때 블러 반경과 크기를 함께 애니메이션하여 표면이 단순한 불투명도 페이드가 아닌 실제 재료가 도착하는 것처럼 읽히도록 하세요.
.toolbar {
  background: rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(20px) saturate(180%);
  border-top: 1px solid rgba(255, 255, 255, 0.4); /* 밝은 상단 가장자리 = 재료에 닿는 빛 */
}

13. 다중 감각 피드백 — 모션 + 소리 + 햅틱

감각을 결합하기 위한 세 가지 규(Designing Audio-Haptic Experiences에서):

  1. 인과성(Causality) — 피드백의 원인이 무엇인지 명확해야 합니다. 실제 원인 이벤트(토글이 뒤집히는 순간, 항목이 제자리에 스냅되는 순간)에 트리거하고, 그 특성을 동작의 물리성과 일치시키세요.
  2. 조화(Harmony) — 시각, 소리, 햅틱은 같은 프레임에서 발생해야 합니다. 이들 사이의 지연은 환상을 파괴합니다.

[이하 생략됨 - 번역 완료]

관련 스킬