본문으로 건너뛰기
TDSMoring 디자인 시스템

PrimaryButton

서비스 주색 그라데이션으로 채운 알약 모양 버튼이다. 한 화면에서 가장 중요한 행동 하나에 쓴다.

이 페이지의 목차
분류
버튼
가져오기
import { PrimaryButton } from '@/ui/Button'
코드 위치
apps/mobile/src/ui/Button.tsx:23

미리보기#

실제 컴포넌트를 화면에 그린 것이다. 화면 아래의 서비스 색 선택기로 색을 바꿔 볼 수 있다. 버튼과 입력 칸은 직접 눌러 볼 수 있다.

화살표 있음 (기본)
화살표 없음
앞쪽 아이콘
높이 56

언제 쓰는가#

쓴다

  • 사용자가 다음에 해야 할 가장 중요한 행동 하나에 쓴다. 예: "저장", "다음으로", "동의하고 시작하기".
  • 한 화면에 하나만 둔다. 화면 아래에 고정할 때는 Screen의 footer에 넣는다.
  • 저장이나 전송처럼 결과를 기다리는 동안에는 busy를 켠다. 글자가 스피너로 바뀌고 다시 눌리지 않는다.
  • 다음 화면으로 넘어가는 행동에는 화살표를 둔다(기본값). 저장, 확인, 재시도처럼 화면을 넘기지 않는 행동에는 arrow={false}를 쓴다.

쓰지 않는다

  • 한 화면에 두 개 이상 놓지 않는다. 두 번째 행동에는 SecondaryButton이나 TextButton을 쓴다.
  • 삭제처럼 되돌릴 수 없는 행동에 쓰지 않는다. dialog.confirm을 tone: "danger"로 호출해 한 번 더 확인한다.
  • 목록 행 안에 쓰지 않는다. 행 안의 작은 행동에는 SmallButton을 쓴다.
  • 사용할 수 없는 이유를 숨긴 채 비활성으로 두지 않는다. 이유를 근처 글자로 알린다.

종류#

PrimaryButton 종류
이름설명
화살표 있음 (기본)arrow가 true다. 글자 뒤에 화살표 아이콘이 붙는다. 다음 화면으로 넘어가는 행동에 쓴다.
화살표 없음arrow={false}. 저장, 확인, 재시도처럼 화면을 넘기지 않는 행동에 쓴다. 앱에서 가장 많이 쓰는 모양이다.
앞쪽 아이콘icon에 17~18px 아이콘을 넣는다. 글자 앞에 놓인다. 아이콘 색은 직접 white로 지정한다.

크기#

PrimaryButton 크기
이름값설명
기본높이 52height를 생략하면 52다.
시트·하단 영역높이 54, 56앱의 시트와 하단 고정 영역에서 height={54} 또는 height={56}을 쓴다. 이 외의 값은 쓰지 않는다.

상태#

기본
누름
비활성
처리 중
PrimaryButton 상태
상태모습과 동작
기본그라데이션 배경, 그림자 shadow(1).
누름0.985배로 살짝 줄어든다. 그림자와 색은 그대로다. 누르는 순간 가벼운 진동(haptic.tap)이 난다.
비활성투명도 0.45. 눌러도 아무 일도 일어나지 않고 진동도 없다.
처리 중글자 대신 스피너를 보여 준다. 크기는 그대로이고 눌러도 반응하지 않는다.

사용 방법#

기본

기본
import { PrimaryButton } from '@/ui/Button';

<PrimaryButton label="다음으로" onPress={next} />

저장 버튼: 처리 중과 비활성

저장 버튼: 처리 중과 비활성
<PrimaryButton
  label="저장"
  arrow={false}
  busy={saving}
  disabled={!changed}
  onPress={() => void save()}
/>

화면 아래에 고정

화면 아래에 고정
import { Screen } from '@/ui/layout';

<Screen footer={<PrimaryButton label="완료" arrow={false} height={56} onPress={done} />}>
  {/* 화면 내용 */}
</Screen>

속성(props)#

PrimaryButton 속성
이름타입기본값필수설명
labelstring—필수버튼에 쓰는 글자. 한 줄로 보여 주고, 길면 글자를 80%까지 줄인다.
onPress() => void—선택누르면 실행할 함수. 없거나 disabled, busy이면 눌러도 실행하지 않는다.
disabledboolean—선택true이면 투명도 0.45로 바꾸고 누를 수 없게 한다.
busyboolean—선택true이면 글자 대신 스피너를 보여 주고 누를 수 없게 한다. 접근성 상태에도 busy가 전달된다.
arrowbooleantrue선택false이면 글자 뒤의 화살표를 숨긴다.
iconReactNode—선택글자 앞에 놓을 아이콘. 흰색(color.white)으로 그린다.
styleStyleProp<ViewStyle>—선택바깥 여백이나 배치만 바꾸는 React Native 스타일이다. 색, 높이, 모서리는 바꾸지 않는다.
heightnumber52선택버튼 높이(px). 앱은 52, 54, 56만 쓴다.
accessibilityHintstring—선택화면 읽기 프로그램이 버튼 이름 뒤에 읽어 주는 설명.

치수와 토큰#

높이
52 (기본)
모서리
pill(999)
좌우 안쪽 여백
20
글자
button (15/20, Bold), 색 white(웹에서는 onPrimaryStrong)
배경
gradient: gradientFrom → gradientTo, 왼쪽에서 오른쪽(90°)
그림자
shadow(1)
아이콘
ArrowRight 18px, 선 두께 2.4
글자와 아이콘 사이
6

접근성#

  • 역할은 button이다. 접근성 상태에 disabled와 busy가 들어간다.
  • 버튼 높이가 52 이상이라 눌러야 하는 영역의 최소 크기(48×48)를 넘는다.
  • 앱은 터치 화면이라 마우스 올림(hover)과 키보드 포커스 표시가 없다. 웹에서 같은 모양을 만들 때는 포커스 링(2.5px 실선, 바깥쪽 3px 띄움)을 직접 그린다. 이 문서의 미리보기에는 포커스 링이 그려져 있다.
  • Android에서는 흰색 물결(ripple)이 눌림 표시를 대신한다. 이때 누름 크기 변화(0.985배)는 유지된다.

글자 색 대비

글자 크기별 기준: 작은 글자는 4.5:1 이상, 큰 글자(굵은 18.66px 이상)는 3:1 이상이다. 값은 선택한 서비스와 모드의 토큰으로 계산했다.

PrimaryButton 글자 색 대비
쓰임글자색바탕대비기준
버튼 글자 (15px 굵게)onPrimaryStronggradient (양 끝 중 낮은 값)3.08:1 미달3.08:1 미달4.60:1 통과5.48:1 통과4.5:1 (작은 글자)

잘한 예와 잘못한 예#

잘한 예가장 중요한 행동 하나만 PrimaryButton으로 하고, 나머지는 글자 버튼으로 낮춘다.
잘한 예저장 중에는 같은 자리에서 스피너로 바꾼다. 버튼 크기가 그대로라서 화면이 흔들리지 않는다.
잘못한 예한 화면에 PrimaryButton이 둘이면 무엇이 중요한지 알 수 없다.
잘못한 예삭제 같은 되돌릴 수 없는 행동에 쓰지 않는다. 위험한 행동은 확인 대화상자를 거친다.
  • SecondaryButton: 테두리만 있거나 옅은 주색으로 채운 알약 모양 버튼이다. PrimaryButton 옆의 두 번째 행동에 쓴다.
  • TextButton: 글자만 있는 가장 가벼운 버튼이다. "나중에", "건너뛰기" 같은 선택 행동에 쓴다.
  • SmallButton: 목록 행 안에 놓는 작은 사각형 버튼이다. "풀기", "승인", "거절" 같은 한 단어 행동에 쓴다.
  • Screen: 모든 화면의 바탕이다. 안전 영역, 키보드, 스크롤, 좌우 여백, 아래 고정 영역을 한 번에 처리한다.
  • dialog: 화면 가운데 뜨는 확인 대화상자와 아래에서 올라오는 메뉴를 함수 호출 하나로 여는 도구다. 결과(눌린 버튼)를 약속(Promise)으로 돌려준다.