버튼은 화면에서 사용자가 실제로 손을 대는 지점이라, 생김새보다 반응이 더 중요합니다. 이번에는 눌리고 싶게 생기고, 눌렀을 때 확실히 답하는 버튼을 상태별 CSS로 만들어 보겠습니다. 아래 코드는 그대로 붙여 확인할 수 있습니다.

11.1 기본 버튼 뼈대 잡기

버튼은 여백과 커서만 제대로 잡아도 절반은 됩니다. 브라우저 기본 스타일은 밋밋하니 최소한의 형태를 만들어 줍니다.


.btn {
padding: 10px 18px;
border: none;
border-radius: 8px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: background .15s, transform .05s;
}


안쪽 여백이 글자를 감싸 누를 판을 넉넉히 만들고, cursor: pointer가 이건 누르는 것이라고 알려 줍니다. transition은 뒤에서 쓸 반응을 미리 준비해 둔 것입니다.

11.2 주요 버튼과 보조 버튼 나누기

한 화면에 강조 버튼이 여럿이면 아무것도 강조되지 않습니다. 주된 행동 하나만 색을 채우고 나머지는 물러서게 합니다.


.btn-primary {
background: #2563eb;
color: #fff;
}
.btn-secondary {
background: #fff;
color: #374151;
border: 1px solid #d1d5db;
}


꽉 찬 파랑이 시선을 끌고 테두리만 있는 버튼은 조용히 곁을 지킵니다. 색의 무게 차이가 곧 우선순위입니다.

11.3 hover와 active로 손끝에 답하기

마우스를 올렸을 때와 누르는 순간의 반응이 있어야 버튼이 살아 있는 느낌을 줍니다.


.btn-primary:hover {
background: #1d4ed8;
}
.btn-primary:active {
transform: translateY(1px);
}


손을 올리면 색이 짙어지고, 누르는 순간 1px 내려앉아 실제로 눌린 듯한 촉감을 흉내 냅니다. 이 작은 움직임이 반응했다는 신호가 됩니다.

11.4 키보드 포커스 드러내기

마우스를 안 쓰는 사람에게는 지금 어느 버튼에 있는지가 보여야 합니다. 기본 외곽선을 지우기만 하면 이들이 길을 잃습니다.


.btn:focus-visible {
outline: 2px solid #1d4ed8;
outline-offset: 2px;
}


focus-visible는 키보드로 이동했을 때만 또렷한 테두리를 그려 줍니다. 마우스 클릭 때는 나타나지 않아 거슬리지 않습니다.

11.5 못 누르는 상태 표시하기

지금 누를 수 없는 버튼은 그렇게 보여야 합니다. 멀쩡해 보이는데 안 눌리면 사용자는 고장인 줄 압니다.


.btn:disabled {
background: #93c5fd;
color: #eff6ff;
cursor: not-allowed;
transform: none;
}


흐려진 색과 금지 커서가 지금은 안 된다고 말해 줍니다. transform: none으로 눌림 효과도 꺼서 반응 없음을 분명히 합니다.

11.6 처리 중 상태 보여 주기

제출을 누른 뒤 아무 변화가 없으면 사용자는 또 누릅니다. 진행 중임을 버튼 안에 담아 두면 중복 클릭이 줄어듭니다.


.btn.is-loading {
color: transparent;
position: relative;
}
.btn.is-loading::after {
content: "";
position: absolute;
inset: 0;
margin: auto;
width: 16px;
height: 16px;
border: 2px solid #fff;
border-top-color: transparent;
border-radius: 50%;
animation: spin .6s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}


글자를 투명하게 감추고 그 자리에 도는 원을 올려 처리 중임을 알립니다. 버튼 크기는 그대로라 레이아웃이 흔들리지 않습니다.

11.7 위험한 행동은 색으로 경고하기

삭제나 탈퇴처럼 되돌리기 어려운 행동은 색으로 미리 긴장을 줍니다.


.btn-danger {
background: #dc2626;
color: #fff;
}
.btn-danger:hover {
background: #b91c1c;
}


빨강은 잠깐 멈춰 생각하게 만듭니다. 주요 파랑 버튼과 나란히 두면 성격이 다른 행동임이 한눈에 구분됩니다.

11.8 좁은 화면에서 버튼 쌓기

가로로 놓인 버튼이 좁은 화면에서 삐져나오면 가로 스크롤이 생깁니다. 폭이 좁아지면 세로로 쌓고 폭을 채웁니다.


.actions {
display: flex;
gap: 10px;
}
@media (max-width: 480px) {
.actions {
flex-direction: column;
}
.actions .btn {
width: 100%;
}
}


480px 아래에서는 버튼이 세로로 서고 화면 폭을 꽉 채웁니다. 손가락으로 누르기 좋은 큰 표적이 됩니다.

11.9 아이콘과 글자 함께 세우기

버튼 안에 아이콘과 글자를 같이 둘 때 정렬이 어긋나면 조잡해 보입니다. flex로 한 축에 맞추고 간격을 줍니다.


.btn {
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn svg {
width: 1em;
height: 1em;
fill: currentColor;
}


아이콘 크기를 1em으로 두어 글자와 나란히 서고, currentColor로 버튼 색을 그대로 물려받습니다. 상태가 바뀌어 색이 변해도 아이콘이 함께 따라옵니다.

11.10 링크를 버튼처럼 보이게 하기

이동하는 링크지만 버튼 모양이 필요할 때가 있습니다. a 태그에도 같은 스타일을 입히되 줄 밑줄만 정리해 줍니다.


a.btn {
display: inline-flex;
align-items: center;
justify-content: center;
text-decoration: none;
color: #fff;
background: #2563eb;
}
a.btn:hover {
background: #1d4ed8;
}


inline-flex와 가운데 정렬로 글자가 판의 중앙에 놓이고, 밑줄을 지워 버튼처럼 보입니다. 실제로는 링크라 새 탭 열기 같은 기본 동작을 그대로 씁니다.

11.11 정리

버튼은 hover, active, focus-visible, disabled, 처리 중까지 상태마다 다른 얼굴을 갖출 때 비로소 믿음직해집니다. 색으로 우선순위와 위험을 나누고, 좁은 화면에서는 쌓아 주는 것까지가 한 세트입니다. 잘 만든 버튼은 설명 없이도 눌러도 된다고 말해 줍니다.