Lyotic · 스타일
모션
움직임은 같은 업무 대상의 상태 변화를 설명합니다. 로딩을 감추거나 AI의 존재감을 과시하기 위해 표면을 계속 움직이지 않습니다.
열두 가지 규칙
MOT-01같은 Work Object의 표면이 pill, lens, card, 검증 범위 badge로 이어집니다. 위치·크기·반경은 duration 0.55, bounce 0.12의 spring, chrome은 0.28초 crossfade를 사용하며 새 목표는 현재 속도에서 이어집니다.
MOT-02콘텐츠는 빠르게 나가고 천천히 들어옵니다. Exit 0.2초, blur 4px, scale 0.985; entrance 0.34초, 아래 4px와 blur 4px에서 50ms 후 시작합니다. 행은 50ms 간격입니다. CSS는 강한 ease-out, 색은 ease를 사용하며 ease-in은 쓰지 않습니다.
MOT-03행 간격은 30–80ms입니다. 일반 기준은 45ms, shapeshift 안에서는 50ms이며 지연이 입력을 막지 않습니다.
MOT-04scale(0)에서 생겨나는 표현은 사용하지 않습니다. 일반 콘텐츠는 작은 offset과 blur로, popover는 호출 지점의 scale(0.97)에서 나타납니다. 누름 피드백은 scale(0.97), 140ms입니다.
MOT-05시스템은 주의를 요청하고 polite 상태 메시지를 보내되 포커스를 옮기지 않습니다. 사람이 요청한 탐색은 포커스의 이동을 따릅니다. 쉬고 있는 pill을 hover·focus하면 lens를 열고 벗어나면 닫습니다.
MOT-06방금 바뀐 상태는 약 1.1초 강조한 뒤 약 420ms 동안 배경과 선만 잦아듭니다. 위치와 label은 움직이지 않습니다.
MOT-07주요 제어는 모핑 중에도 제자리를 유지합니다. 포인터 아래에서 다른 행동으로 바뀌지 않습니다. INV-09와 함께 검사합니다.
MOT-08키보드로 호출한 표면은 움직이지 않습니다. Hover 효과는 (hover: hover) and (pointer: fine) 조건에서만 적용합니다.
MOT-09Reduced motion에서는 공간·크기 spring을 제거하고 목표 크기로 바로 이동합니다. 불투명도·색 변화는 150ms 이하로 줄이며 움직임을 보지 않아도 상태를 읽을 수 있어야 합니다.
MOT-10일반 UI 전환은 300ms 미만입니다. 셸의 공간 모핑과 설명용 시퀀스는 예외입니다.
MOT-11핫 패스에서는 transform, opacity, filter, clip-path만 애니메이션합니다. 예외는 contain: layout을 가진 셸 한 컨테이너의 크기 spring입니다. 자식 요소의 레이아웃은 애니메이션하지 않습니다.
MOT-12실제 실행이 진행 중일 때만 해당 대상에 spinner를 표시합니다. outcome-unknown은 정지된 상태이며 계속 회전하지 않습니다.
형태 전환
Morphing은 이 의미 구조가 렌더러에서 드러나는 방식 중 하나다. 업무 상태가 다른 이해나 제어를 요구할 때 표현이 변하고, 그 변화가 같은 객체의 연속성으로 읽히도록 움직임을 설계한다. 로딩을 감추기 위해 의미 없는 변형을 반복하거나, AI가 작동 중이라는 인상을 주기 위해 컨테이너를 계속 움직이는 것은 Lyotic의 목적과 관계없다. 움직임은 상태의 의미를 설명해야 한다.
아래 실험의 수치는 시각·모션 참조를 살펴보기 위한 것입니다. 값을 조절했다고 사람의 이해가 개선되었다는 평가 결과가 생기지는 않습니다.
곡선
CSS 전환은 강한 ease-out을 사용합니다. 인터페이스가 입력에 늦게 반응하는 ease-in은 사용하지 않습니다.
시간
일반 UI 전환은 300ms 미만입니다. 공간적 shapeshift와 설명 목적의 시퀀스는 예외이며, 키보드로 호출한 표면은 움직이지 않습니다.
누르기
누름 피드백은 scale(0.97), 약 140ms를 기준으로 합니다. 0에서 확대해 생겨나는 효과는 사용하지 않습니다.
코드
공용 Shapeshift와 spring을 가져와 사용합니다. initiator, 키보드의 instant, reduced-motion 조건을 실제 호출 경로에서 전달해야 합니다. API가 지원한다는 사실만으로 모든 호출부가 규칙을 지키는 것은 아닙니다.
import { Shapeshift, SPRINGS, settleLit } from '@lyotic/morph';
const shift = new Shapeshift(surface, { stage: table });
shift.show('pill', { at: statusCell, ax: 0, ay: 0.5, bg: 'warn', r: 999 }); // rests in its row
shift.show('card', { at: statusCell, ax: 1, ay: 0, bg: 'card', r: 12, // grows out of the pill
initiator: 'system', announce: plan.reason }); // no focus change
shift.show('done', { at: statusCell, ax: 0, ay: 0.5, bg: 'good', r: 999 }); // folds back, lit
setTimeout(() => shift.chrome('none'), 1650); // …then rests