Skip to content

Buttons

Hereby 버튼은 하나의 통합 체계예요. 단일 베이스 ds-btn 위에 변형 × 색 × 크기 modifier만 조합해서 만들어요. "모달 버튼" 처럼 맥락별로 따로 만든 클래스는 없어요 — 맥락은 사용 규칙으로 풀고, 버튼 자체는 변수 조합이에요.

html
<button class="ds-btn  ds-btn--solid  ds-btn--primary  ds-btn--md">저장</button>
<!--          베이스    변형           색              크기        -->

상태(hover·focus·pressed·disabled)는 마크업이 필요 없어요. 전부 CSS에서 공통 인터랙션 오버레이로 처리돼요. hover:bg-* 같은 유틸을 따로 붙이지 마세요.

기본 ds-btn과 다르게 쓰이는 조합이 있어요

버튼 변형을 여러 개 조합해 만든 실제 컴포넌트(소속 칩·리스트 카드)는 단일 버튼과 쓰임새가 달라요 — 값을 보여주면서 동시에 조작하는 역할을 하고, 맥락에 따라 구성 조각이 빠지기도 해요. 단일 버튼 규칙만 보고 만들면 어긋나니, 이런 조합을 만들기 전에 아래 예시를 먼저 보세요.

예시 1 · 소속 칩 ↓예시 2 · 리스트 카드 ↓

출처

구현: packages/static/styles/design-system.cssHereby Button System (unified) 블록. 설계 narrative: docs/reference/button-system-spec.md. Figma 5노드(solid 9010-10531 · line 9017-10430 · text 9036-1306 · interaction 9010-10532 · secondary 팔레트 9014-11311 · icon 9803-8531).

규칙 (강제) · 코딩 에이전트용 — 사람은 접힌 채로 두어도 됩니다

버튼 강제 규칙. 상세·미리보기·전체 조합표는 아래 본문.

적용 범위 — 신규 코드 기준. 아래 규칙은 새로 작성·수정하는 버튼에 강제됩니다. 앱에 남아 있는 레거시 버튼(ds-btn--secondary·icon-btn·날 유틸 조합)은 "규칙 위반"이 아니라 마이그레이션 대상입니다 — 마이그레이션 매핑대로 교체하되, 이미 작업 중인 파일 안에 있을 때만 손대세요. 레거시 버튼을 찾아 다니며 전수 교체하지 마세요(별도 PR 예정). 특히 icon-btn(28px) → ds-btn--icon(24px)은 크기가 바뀌는 시각 변경이라 임의로 밀면 안 됩니다.

조합 (신규 — 강제)

  • 버튼은 베이스 + 변형 + 색 + 크기 4축을 모두 명시: ds-btn ds-btn--solid ds-btn--primary ds-btn--md. 축을 빠뜨리면 색·크기가 결정되지 않습니다.
  • --line은 반드시 명시적 톤과 함께(--normal/--dark/--red …). 톤 없는 ds-btn--line은 색이 안 정해집니다.
  • text 변형은 ds-btn-text 별도 패밀리(ds-btn--text 아님) — 폰트 스케일이 달라서 클래스 세트가 분리돼 있습니다.
  • 날 유틸 즉석 조합 금지: bg-orange-500 px-3 py-1.5 text-white rounded 같은 조합으로 버튼을 새로 만들지 마세요. 즉석 색 pill(text-[10px] bg-orange-100 …)도 새로 만들지 마세요.
  • 상태(hover/focus/pressed/disabled)에 마크업 금지 — CSS 공통 인터랙션 오버레이가 처리합니다. hover:bg-* 유틸을 붙이지 마세요.
  • 맥락 → 조합 매핑(모달 푸터·삭제·툴바 추가 등)은 사용 규칙 표를 따르세요.
  • "여러 선택지 중 하나를 고르는 2줄 카드"는 ds-option-card — 테두리·hover를 유틸로 직접 그리지 마세요. 크기는 compact(기본) / --lg 2종뿐이고(크기 2종) 중간값을 새로 만들지 않습니다. min-height 금지(내용만큼 늘어남), 제목줄 아이콘(__icon)은 선택이며 카드마다 같은 아이콘이면 넣지 마세요.

⚠️ 트랩 — 모르면 반드시 깨지는 것

  • <button> radius는 4px로 전역 강제입니다. design-system.css의 blanket 규칙 3개가 각각 걸어요 — button:not([class*="bg-"]):not(.ds-tab-item)… · button[class*="border"]:not(.ds-tab-item) · button[class*="bg-"]:not([class*="border"]). Tailwind rounded-*를 줘도 덮어써집니다. 제외 명단: .ds-tab-item · .ds-sub-tab__item · .ds-menu__item · .icon-btn · .ds-btn--icon · .ds-btn--circle · .org-tree__action-btn · .ds-page-header__avatar. 모서리를 바꾸려면 모서리 축 ds-r-8/12/16/999(+ 되돌리기용 ds-r-4)를 쓰세요 — 세 규칙이 값을 var(--ds-btn-radius, 4px)로 읽으므로 !important 없이 이깁니다. rounded-*·!important·<div role="button"> 우회는 쓰지 마세요(축에 없는 값이 꼭 필요할 때만 엘리먼트 교체 고려). 상세: 모서리 → 트랩.
  • bg-* 클래스가 없는 <button>은 DS 크기 clamp에 걸립니다(button:not([class*="bg-"])): 폰트 크기 유틸에 따라 min-height/padding이 강제돼요 — text-[10px]→14px · text-xs/text-[12px]→21px · text-[13px]→22px · text-sm→24px · text-base→26px, padding 2px 고정. 통합 체계(ds-btn--*)를 쓰면 걸리지 않습니다.
  • disabled 배경: .ds-btn:disabledbackground: var(--color-line-normal)(#e5e5e5 회색 박스) + color: var(--color-label-disable)로 강제됩니다. 투명 배경이 필요하면 ds-btn--icon을 쓰세요(.ds-btn--icon:disabledbackground: transparent).
  • line 버튼 disabled 보더는 #c1c1c1 solid가 아니라 40% 불투명도(var(--ds-tint-light-40) = rgba(193,193,193,0.4), Figma 9032:2225). 직접 #c1c1c1로 칠하지 마세요.
  • !important 금지 — 앱 main.css@import 뒤에 오므로 소스 순서로 이미 이깁니다. 캐스케이드가 안 먹으면 !important 대신 셀렉터/엘리먼트를 바꾸세요.
  • 버튼 CSS 복제 주의: 버튼은 정본(design-system.css)에만 있지만, 같은 계열인 .ds-page-btn(페이지네이션)은 앱 5곳에 복제돼 있습니다. 상세: .claude/rules/frontend/design-system-architecture.md.

버튼 탐색기

변형을 클릭으로 골라 모든 색 × 크기(52 → 20px) 를 한눈에 보세요. 실제 design-system.css 클래스가 그대로 붙어 있어서 hover·focus·pressed·disabled가 라이브로 동작해요.

변형
모서리
primary
gradient
dark
assistive
muted
white-bk
white-or
lightor
xl52
lg48
ml40
md32
sm28
xs20

통합 체계 ds-btn + --변형 + --색 + --크기를 실 design-system.css 클래스로 렌더. 사이즈 52 → 20px 내림차순. 상태는 버튼에서 직접 — 마우스 hover · Tab focus · click-hold pressed. disabled 토글. line은 bg 채움으로 보더만 ↔ 흰색 채움 전환. icon은 Figma Button/Icon — 원형, 글리프↔호버박스 ×1.5(12↔18·16↔24·20↔30·24↔36), 4색. 왼쪽/오른쪽 아이콘 토글로 라벨 좌우 아이콘 슬롯을 켜고 끄며 확인 — 사이즈마다 간격·아이콘 크기가 Figma값(xs14·sm18·md/ml20·lg/xl24)으로 자동 적용돼요. 모서리: 기본 4 · ds-r-8 · ds-r-12 · ds-r-16 · ds-r-999(알약). 크기·색과 독립된 축이라 어떤 조합에도 얹을 수 있어요(원형 icon 변형은 999 고정이라 제외). : solid = Figma 8(primary·gradient·dark·assistive·muted·white-bk·white-or·lightor) · line = 7 + secondary 팔레트(red·green·blue·purple·yellow·aquablue, --color-secondary-* Figma 정확값).

라이브 미리보기

대표 조합을 변형별로 모았어요. 마우스 hover·Tab focus·click-hold pressed로 실동작을 확인하세요.

한 베이스 ds-btn변형 × 색 × 크기 modifier만 붙여요. 상태는 마크업이 필요 없어요 — 마우스 hover · Tab focus · click-hold pressed로 공통 오버레이(hover 20% · focus 30% · pressed 10%)가 그대로 보여요.

Solid 주요 확정 액션 (저장·등록) — 대표 32px(md)

Line 보조·위험 액션 · 색으로 의미 구분 — 대표 32px(md)

Text 인라인 CTA · 링크 · 모달 닫기 (별도 폰트 스케일)

Icon 표·툴바 인라인 액션 · 24px 원형 · title 툴팁

클래스 API

클래스필수
베이스ds-btn
변형ds-btn--solid · --line · --icon · (text는 ds-btn-text 패밀리)✅ (1개)
변형별 팔레트 ()✅ (1개)
크기solid/line: --xs --sm --md --ml --lg --xl · text: ds-btn-text--sm/md/lg/xl · icon: --xs --sm --md --lg (글리프↔호버박스 ×1.5)
모서리ds-r-8 · ds-r-12 · ds-r-16 · ds-r-999 (+ 되돌리기용 ds-r-4) (모서리)— (기본 4px)
옵션ds-btn--full(너비 100%) · ds-btn--bg(line 흰색 채움)

Text 변형은 별도 클래스 패밀리예요

text 변형은 폰트 스케일이 달라서 ds-btn--text가 아니라 ds-btn-text 클래스 세트를 써요. 베이스 ds-btn-text + 크기 ds-btn-text--sm/md/lg/xl + 색 ds-btn-text--primary/dark/assistive/white + 굵기 ds-fw-regular/medium/semibold/bold 로 조합해요.

변형 (variation)

같은 색 modifier라도 변형마다 다르게 표현돼요.

SolidLineTextIcon
형태채움 + 흰 글자보더 + 글자색, 투명 bg글자만24px 원형 아이콘
채움 옵션(기본 채움)ds-btn--bg = 흰색 채움
좌우 아이콘leading/trailing— (아이콘 자체가 본체)
용도주요 확정보조·위험인라인 CTA·링크표·툴바 아이콘 액션

조합 예시 — 소속 칩 (.ds-member-chip)

버튼 변형을 조합한 실제 컴포넌트예요. pill 컨테이너 .ds-member-chip 안에 팀·역할 세그먼트(.ds-member-seg--team/--role)와 제거 아이콘(.ds-member-remove)을 담고, 바깥에 ds-btn-text(muted) "소속 추가" 버튼을 둬요. 표는 기본(small), 모달은 .ds-member-shell--lg(large) 두 사이즈예요. 추가 버튼의 라벨 노출은 공간과 맥락에 따라 달라요 — 표(small)에서 칩이 이미 있으면 + 아이콘만(라벨은 title 툴팁), 칩이 하나도 없는 빈 상태거나 **여유 있는 모달(large)**에서는 "+ 소속 추가" 라벨을 함께 노출해요.

small 22
표 기본
·
large 40
모달 · --shell--lg
·
html
<!-- small (표): 기본 .ds-member-shell / large (모달): + .ds-member-shell--lg -->
<div class="ds-member-shell">
  <span class="ds-member-chip">
    <button class="ds-member-seg ds-member-seg--team"><span class="truncate">개발 1팀</span></button>
    <span class="ds-member-sep">·</span>
    <button class="ds-member-seg ds-member-seg--role"><span class="truncate">일반 직원</span></button>
    <button class="ds-member-remove" aria-label="제거">&times;</button>
  </span>
  <!-- 추가 버튼 라벨: 표에 칩이 있으면 아이콘만 (라벨은 title 툴팁) -->
  <!-- 빈 상태(<span x-show="memberships.length === 0">소속 추가</span>)와 모달(large)에서는 라벨을 함께 노출 -->
  <button class="ds-btn-text ds-btn-text--sm" title="소속 추가">
    <svg class="shrink-0 ds-member-add-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 4v16m8-8H4" stroke-linecap="round"/></svg>
  </button>
</div>

위계는 팀=--team(진한 라벨색) · 역할=--role(회색), 강조는 채움이 아니라 색·굵기 축으로. 실제 인라인 편집(팀/역할 피커)은 ds-menu를 참조하세요.

확장성 — 고정된 한 벌이 아니에요

이 조합은 조각을 골라 쓰는 구조예요. 단일 버튼처럼 "이 마크업 그대로"가 아니라, 화면 맥락에 맞춰 아래 축으로 늘이고 줄이세요.

쓸 수 있는 형태
세그먼트 클릭기존 값이 있으면 그 값을 보여주고 클릭 시 변경(피커 오픈) · 값이 없으면 빈 상태로 두고 클릭 시 새로 지정 — 즉 세그먼트는 표시입력을 겸해요
(+) 추가 버튼항목을 늘릴 수 있는 화면에만. 단일 값만 갖는 맥락(하나만 고르는 필드)에서는 없어도 돼요
(×) 제거 버튼지울 수 있을 때만. 필수 값이거나 읽기 전용이면 빼세요
읽기 전용두 버튼을 모두 빼면 값 표시 전용 칩이 돼요

조각을 뺄 때도 남는 조각의 클래스는 그대로 두세요(ds-member-chip / --team / --role). 빠진 자리를 채우려고 여백·크기를 손보지 말고, 필요한 조각만 렌더하면 shell이 정렬을 맞춰줘요.

조합 예시 2 — 리스트 카드 (.ds-list-card)

표로 담기엔 열이 적고 「펼쳐서 상세」가 필요한 목록용 카드예요. 헤더 행에 이름 · 구분선 · 설명 · 상태 라벨을 한 줄로 놓고, 우측에 아이콘 버튼을 나란히 둬요 — 카드 자체는 색·크기 축을 갖지 않고 버튼을 조합해서 쓰는 껍데기예요.

재직증명서현재 재직 중임을 증명하는 문서
퇴직증명서비활성퇴직 사실을 증명하는 문서
조각클래스역할
목록ds-list-card-group카드 세로 나열(gap 8px)
카드ds-list-card테두리·라운드. 상태 is-expanded · is-inactive
헤더 행ds-list-card__header클릭 영역. hover/pressed 틴트 포함
좌측 묶음ds-list-card__info이름·구분선·설명·라벨을 한 줄로
이름 / 구분선 / 설명__name · __divider · __desc넘치면 말줄임
우측 액션ds-list-card__actions아이콘 버튼들을 담는 자리
셰브론ds-list-card__chevron회전만 담당(is-flipped). 버튼 스타일은 ds-btn--icon
펼친 상세ds-list-card__body안쪽 내용은 화면마다 다름 — 여백만 정해요

액션은 필요한 것만

세 버튼이 고정 세트가 아니에요. 수정만 있어도, 셰브론만 있어도 됩니다(위 두 번째 카드처럼). 빠진 자리를 채우려고 여백을 손보지 말고 필요한 버튼만 렌더하면 __actions가 정렬을 맞춰줘요.

표(Table 1)와 헷갈리지 마세요

열이 3개 이상이고 값을 세로로 비교해야 하면 Table 1이 맞아요. 리스트 카드는 한 줄 요약 + 펼쳐서 상세가 필요할 때예요. 목록이 길어지면 카드 목록도 스크롤 박스(.ds-table-scroll)에 넣어 상단 액션 줄을 고정하세요.

사용처: admin 역할 관리(권한 pill을 펼쳐 보여줌), 문서 양식 › 문서 제출 유형(양식 미리보기를 펼쳐 보여줌).

조합 예시 3 — 선택 카드 (.ds-option-card)

여러 액션 중 하나를 고르는 2줄(제목 + 보조설명) 카드예요. 카드 전체가 하나의 클릭 대상(=액션 실행)이라, 실제 ds-btn 은 아니지만 아웃라인·폰트·hover·pressed 를 ds-btn--line(normal 색)과 1:1로 맞춘 껍데기예요. 버튼은 단일 라인이라 "제목+설명 2줄"이 안 들어가서 별도 카드로 두되, 인터랙션은 버튼과 똑같이 읽히게 했어요.

크기 · 아이콘 · 배치를 클릭해서 바로 비교하세요. 두 크기가 항상 같은 조건으로 나란히 렌더돼요.

설명
배치
compact (기본)패딩 8/12 · 제목 13
확대 --lg패딩 12/16 · 제목 14

실제 .ds-option-card 클래스를 그대로 렌더해요 — 마우스 hover · click-hold pressed · disabled 토글이 라이브로 동작합니다. 크기는 2종뿐(compact / --lg)이고 패딩과 제목 크기만 달라요 — 테두리·라운드·hover·보조설명(12px)은 동일. 중간 크기를 새로 만들지 마세요. 제목줄 아이콘은 선택 — 카드마다 다른 아이콘일 때만 켜세요(전부 같은 아이콘이면 정보량이 0이라 끕니다). 그리드 배치에서 설명 길이를 여러 줄로 바꿔 보면, 카드 높이는 줄 단위로 맞춰지되 내용은 위로 정렬되는 걸 확인할 수 있어요(min-height 금지 이유).

html
<button class="ds-option-card" @click="exportCsv()">
  <div class="ds-option-card__title">CSV — 현재 보이는 행</div>
  <div class="ds-option-card__desc">검색 / 필터로 좁힌 결과만 내보냅니다</div>
</button>
조각클래스역할
카드ds-option-card테두리(line-normal)·라운드 4px·13px/500·hover·pressed. 카드 전체가 클릭 대상
제목ds-option-card__title첫 줄. 카드 base(13px/500 · label-normal)를 그대로 씀
보조설명ds-option-card__desc둘째 줄. 12px(--text-b6) · label-muted
제목줄 아이콘 (선택)ds-option-card__icon제목 왼쪽 16px(lg 18px) 슬롯. label-muted

크기 2종

크기클래스패딩 · 제목언제
compact (기본)ds-option-card8/12px · 13px세로로 쌓는 목록형 선택지. 한 줄 설명, 모달 하단에 여러 개
확대ds-option-card--lg12/16px · 14px그리드 갤러리형 카드. 설명이 2~3줄, 카드가 그 화면의 주요 콘텐츠

늘어나는 건 패딩과 제목 크기뿐이에요. 테두리·라운드·hover·pressed·보조설명(12px)은 두 크기가 같습니다. 그 사이 값을 새로 만들지 마세요 — 둘 중 가까운 쪽을 고릅니다. 두 크기를 나란히 놓고 보려면 위 탐색기배치 · 설명 · 아이콘 토글을 쓰세요.

html
<!-- 확대 + 제목줄 아이콘 -->
<button class="ds-option-card ds-option-card--lg" @click="applyTemplate(tpl)">
  <div class="ds-option-card__title">
    <span class="ds-option-card__icon" x-html="icon('ic-file-text')"></span>
    <span x-text="tpl.name"></span>
  </div>
  <div class="ds-option-card__desc" x-text="tpl.description"></div>
</button>

아이콘은 "끄는 게 기본"이에요

아이콘 슬롯은 선택입니다. 카드마다 같은 아이콘이 반복될 뿐이면(예: 템플릿 목록의 문서 아이콘) 정보량이 0이라 넣지 마세요. 카드마다 다른 아이콘이 붙어서 훑을 때 분류가 되는 경우에만 씁니다. 슬롯을 비우면 제목은 원래대로 한 줄 텍스트로 렌더돼요(빈 슬롯을 위한 자리 확보 같은 건 없습니다).

카드 높이는 고정하지 않습니다. 내용만큼 늘어나고, 그리드에 깔면 같은 줄끼리 자동으로 높이가 맞아요(min-height를 박으면 설명이 긴 카드가 잘립니다).

리스트 카드(.ds-list-card)와 헷갈리지 마세요

.ds-list-card"한 줄 요약 + 펼쳐서 상세"(레코드 + 우측 아이콘버튼 + 펼침)예요. .ds-option-card"펼침 없이 카드 전체가 하나의 액션" — 라디오/메뉴처럼 여러 선택지 중 하나를 고를 때 씁니다. 액션이 한 줄로 충분하면 카드 대신 ds-btn--line 버튼을, 목록/펼침이 필요하면 ds-list-card 를 쓰세요.

사용처: hr-system 다운로드(내보내기) 모달의 CSV / XLSX / JSON 선택지.

색 (color)

색은 변형마다 쓸 수 있는 팔레트가 달라요.

Solid

ds-btn--primary · --dark · --assistive · --muted · --white-bk · --white-or · --lightor · --gradient

Line

ds-btn--primary · --dark · --assistive · --muted · --normal · --white · --red · --green · --blue · --purple · --yellow · --aquablue (+ ds-btn--bg = 흰색 채움)

Icon

기본 dark, 그리고 ds-btn--white · --muted · --primary.

secondary 의미색도 solid·line과 똑같이 확장돼요: ds-btn--red · --green · --blue · --purple · --yellow · --aquablue. 아이콘 글리프 색이 해당 secondary 토큰으로 바뀌어요(예: 위험한 인라인 삭제 아이콘 = ds-btn--red).

Text

ds-btn-text--primary · --dark · --assistive · --white (기본은 muted).

레거시 색은 secondary로 매핑해요

green·purple·blue·yellow·aquablue는 Figma secondary 공식 색이라 정상이에요. 단 gray-*neutral, amber-*yellow, emerald/indigo/teal는 해당 secondary로 바꿔야 해요. 자세한 건 색 토큰 컨벤션을 참고하세요.

크기 (size)

solid·line은 높이 기준 6단계예요.

token높이
ds-btn--xs20px
ds-btn--sm28px
ds-btn--md32px (기본)
ds-btn--ml40px
ds-btn--lg48px
ds-btn--xl52px

모바일에서는 ds-btn--lg를 기본으로 권합니다 — 데스크톱의 ds-btn--md·ds-btn--ml은 마우스 포인터 기준 크기라 손가락으로는 작습니다(크기는 픽셀이 아니라 이 클래스로 지정하세요. 버튼은 영문 표기, 폼 컨트롤은 숫자 표기 ds-input--48). 모바일 화면(직원 앱)과 모바일 풀페이지 모달의 CTA는 ds-btn--lg + 풀폭(ds-btn--full, 또는 푸터의 flex: 1)이 출발점이에요(강제는 아니니 화면이 요구하면 다른 크기를 골라도 됩니다). 데스크톱과 같은 마크업을 쓰는 화면에서 크기만 바꾸는 법은 → Modal → 모바일.

icon은 같은 --xs/--sm/--md/--lg 토큰을 쓰되 글리프(아이콘) ↔ 호버 박스가 ×1.5로 묶여요(원형 호버 영역 = 아이콘의 1.5배). 기본은 --sm(16↔24)이에요.

token글리프호버 박스
ds-btn--xs12px18px
ds-btn--sm16px24px (기본)
ds-btn--md20px30px
ds-btn--lg24px36px

text는 별도 폰트 스케일이에요: ds-btn-text--sm(12px) · --md(13px) · --lg(14px) · --xl(16px), 기본 10px. 높이 없는 인라인이에요.

모서리 (radius)

버튼 모서리는 기본 4px이에요(2026-07-13 확정 — 8/12/16/999를 비교한 뒤 기본값은 4px로 유지하기로 했어요). 다른 모서리가 필요한 버튼에만 ds-r-* 를 하나 얹으세요. 변형·색·크기와 독립된 축이라 어떤 조합에도 붙일 수 있어요.

html
<button class="ds-btn ds-btn--solid ds-btn--primary ds-btn--md  ds-r-12">저장</button>
<!--                                                             모서리 12px -->
클래스모서리
(없음)4px (기본)
ds-r-44px — 기본값과 같아요. 아래 「컨테이너 상속」으로 다른 모서리가 걸린 안에서 이 버튼만 기본으로 되돌릴 때만 씁니다
ds-r-88px
ds-r-1212px
ds-r-1616px
ds-r-999999px — 알약(pill)

버튼 탐색기모서리 컨트롤로 색·크기 전 조합에 실시간으로 적용해볼 수 있어요.

컨테이너에 얹으면 그 안의 버튼이 전부 바뀌어요 — 레거시 버튼까지

ds-r-*--ds-btn-radius 변수만 세팅하고, 버튼의 border-radius 선언이 그 변수를 읽어요(굵기 축 ds-fw-* 와 같은 구조). CSS 변수는 상속되므로 버튼을 감싼 컨테이너에 얹으면 그 안의 버튼에 일괄 적용돼요.

여기서 "그 안의 버튼"은 DS 버튼만이 아니라 <button> 전부예요 — 날 유틸(bg-*/border-*)로 만든 레거시 버튼도 전역 강제 규칙을 통해 같은 변수를 읽기 때문이에요. 그래서 툴바·카드에 얹으면 의도하지 않은 주변 버튼까지 모서리가 바뀝니다.

  • 기본은 버튼 엘리먼트에 직접 얹으세요. 컨테이너 적용은 그 안의 버튼을 전부 확인한 뒤에만.
  • 컨테이너 적용 중 특정 버튼만 되돌리려면 그 버튼에 ds-r-4(기본값)를 얹으세요.
  • 화면 전체를 한 번에 바꿔보는 실험(예: 앱 전체를 12px로 미리보기)에는 이 상속이 유용해요 — <body>에 얹으면 됩니다.
  • 변수를 읽지 않는 버튼 외 요소(카드·입력 등)는 영향이 없어요.

원형 아이콘 버튼은 제외예요

ds-btn--icon · ds-btn--circle 은 원형(999px)이 정체성이라 이 축을 무시해요.

상태 (state)

마크업 없이 CSS가 처리해요. 공통 인터랙션 오버레이를 얹는 단일 모델이에요.

상태solidline · text · icon오버레이
hover채움 위 오버레이bg 오버레이20%
focus전역 링 + 오버레이전역 링 + bg 오버레이30%
pressed오버레이bg 오버레이10%
disabled회색 박스line=보더 40% / text·icon=흐리게
  • 오버레이 톤: 일반 색은 muted(hover/focus)→black(pressed), primary 계열은 orange, light 변형(lightred·lightgreen·lightor 등)은 각자 의미색(빨강·초록·주황…)으로 진해져요 — 연한 -50 채움이 hover에 회색으로 바래지 않게.
  • focus 링은 전역 button:focus-visible로 모든 버튼에 자동 적용돼요(키보드 접근성).

아이콘 버튼 (icon)

원형, 아이콘 전용이에요. 버튼 박스가 곧 원형 호버 영역이고, 글리프의 1.5배 크기예요(--sm이면 글리프 16 / 호버 박스 24). 사이즈는 --xs/--sm/--md/--lg(크기), 기본 --sm. title을 주면 싱글톤 툴팁이 자동으로 붙어요.

html
<button class="ds-btn ds-btn--icon ds-btn--dark ds-btn--sm" title="수정" aria-label="수정"><svg>…</svg></button>

색은 dark(기본)·ds-btn--white·ds-btn--muted·ds-btn--primary 4종에 secondary 의미색 ds-btn--red·--green·--blue·--purple·--yellow·--aquablue를 더해 10종이에요(). svg 크기는 사이즈 클래스가 CSS로 잡으니 마크업은 그대로 두면 돼요.

좌우 아이콘 (leading / trailing)

solid·line·text라벨 좌우에 아이콘을 넣을 수 있어요(Figma left/right 슬롯). 라벨 <span> 앞/뒤에 <svg>를 형제로 두면 끝이에요 — 간격(gap)과 아이콘 크기는 사이즈 클래스가 CSS로 잡아요. 별도 클래스나 gap-*·w-* 유틸을 붙이지 마세요.

html
<!-- leading(왼쪽) -->
<button class="ds-btn ds-btn--solid ds-btn--primary ds-btn--md">
  <svg>…</svg><span>추가</span>
</button>

<!-- trailing(오른쪽) -->
<button class="ds-btn ds-btn--line ds-btn--dark ds-btn--md">
  <span>다음</span><svg>…</svg>
</button>

<!-- 좌우 동시 + text 변형 -->
<button class="ds-btn-text ds-btn-text--primary ds-btn-text--md">
  <svg>…</svg><span>필터</span><svg>…</svg>
</button>

사이즈마다 gap × 아이콘 박스가 묶여 있어요(Figma button/solid 9010-10531 · line 9017-10430 · text 9036-1306). solid·line은 같은 값이고, text만 폰트 스케일에 맞춰 gap이 좁아요.

토큰solid · line gap / 아이콘text gap / 아이콘
--xs (text는 기본)2px / 14px2px / 14px
--sm4px / 18px2px / 18px
--md6px / 20px2px / 20px
--ml6px / 20px— (text엔 ml 없음)
--lg8px / 24px4px / 24px
--xl8px / 24px4px / 24px

아이콘만 필요하면 icon 변형

라벨 없이 아이콘 하나만 쓰는 표·툴바 액션은 좌우 슬롯이 아니라 원형 아이콘 버튼(ds-btn--icon)을 쓰세요. 좌우 아이콘은 라벨이 있는 버튼의 보조 표식이에요.

svg 직접 자식만 사이징돼요

gap·크기 규칙은 .ds-btn--solid > svg처럼 직계 <svg> 를 기준으로 해요. 아이콘을 <i>·<span> 래퍼로 감싸면 크기가 안 잡혀요 — <svg>를 라벨과 같은 레벨에 형제로 두세요. 칸(슬롯) 크기만 정하고 안쪽 여백은 강제하지 않아요 — 아이콘마다 자체 여백이 달라서, 드롭한 <svg>가 칸을 그대로 채워요.

사용 규칙 (맥락 → 조합)

버튼은 변수 조합이고, 맥락은 아래 규칙으로 정해요.

맥락조합
모달·얼럿 푸터 권장 컨펌solid · dark · md
모달·얼럿 푸터 크리티컬 컨펌solid · lightor · md
모달·얼럿 푸터 취소·닫기solid · white-bk · md
표/툴바 "추가"solid · primary · sm
인라인 링크 CTAtext · primary
삭제(위험) — 인라인/툴바line · red · md
리스트 행 상세·보기(텍스트 버튼)line · normal · sm
모바일 풀폭 제출solid · primary · lg + ds-btn--full
모바일 모달 푸터 CTA (<768)위 푸터 조합과 같은 색·변형, 크기만 ds-btn--lg + 풀폭 (권장 기본값)
표 행 아이콘 액션icon · dark (또는 muted)

리스트 "보기/상세"는 라인 버튼으로 (즉석 색 pill 금지)

결재/문서 목록 행의 "보기·상세"ds-btn ds-btn--line ds-btn--normal ds-btn--sm으로 통일해요. text-[10px] bg-orange-100 text-orange-600 px-2 py-0.5 rounded 같은 즉석 색 pill을 새로 만들지 마세요. 그리고 --line항상 명시적 톤(--normal/--dark/--red …)을 함께 붙여야 해요(톤 없는 ds-btn--line은 색이 안 정해짐).

모달·얼럿 푸터는 위치가 아니라 맥락으로

가장 우측이라고 항상 orange/dark를 쓰지 않아요. 확정 버튼은 위계(어느 버튼) × weight 티어(중요도: 기본=dark·크리티컬=lightor·물러남=white-bk) 2축으로 정합니다. 의미색 red(위험)·green(승인)은 모달 밖 인라인·툴바·워크플로에 남습니다. 전체 규칙·예시는 모달 → 푸터 버튼 강조를 참고하세요.

마이그레이션 매핑 (old → new)

레거시 버튼은 두 갈래로 남아 있어요: ① bg-orange-500 px-3 py-1.5 … 같은 유틸 즉석 조합, ② ds-btn--secondary·icon-btn 같은 옛 명명 클래스. 둘 다 아래처럼 통합 체계로 바꿔요. 새 코드는 아래 오른쪽 열만 쓰세요.

현재 (레거시)→ 새
ds-btn ds-btn--primary (모달)ds-btn ds-btn--solid ds-btn--primary ds-btn--md
ds-btn ds-btn--secondary (취소)ds-btn ds-btn--line ds-btn--dark ds-btn--md
유틸 solid bg-orange-500 px-3 py-1.5 text-white roundedds-btn ds-btn--solid ds-btn--primary ds-btn--md
유틸 line border border-red-600 text-red-600 px-3 py-1.5ds-btn ds-btn--line ds-btn--red ds-btn--md
ds-btn-text ds-btn-text--primary ds-btn-text--sm(그대로 유지 — text 패밀리가 정식)
icon-btn (28px)ds-btn ds-btn--icon ds-btn--dark (24px)
icon-btn icon-btn--danger (28px)ds-btn ds-btn--icon ds-btn--red (24px — 색은 #e63224로 동일)
토글 칩 (:class 조건부 bg-orange-500 / bg-orange-600)버튼 아님 → 선택 칩으로 별도 처리
마이그레이션 진행 메모

유틸 즉석 조합(bg-orange-500 …, border …)과 옛 명명 클래스는 앱별로 점진 치환 중이에요. DS 연결된 앱부터 옮기고, button[class*="bg-"] … !important override 블록과 앱 main.css 복제본을 걷어내요. 자세한 순서는 docs/reference/button-system-spec.md §12를 참고하세요.