스타일 · CSS · 테두리 · 그림자 · 애니메이션

CSS · 테두리, 그림자, 트랜지션, 애니메이션

앞에서는 글자·색·배경을 칠했어요. 이번 장은 요소에 입체감움직임을 주는 단계입니다. 테두리로 윤곽을 잡고, 그림자로 떠 보이게 하고, 트랜지션·애니메이션으로 부드럽게 변하게 만들어요. CSS? 가 가장 "보여주는 재미"가 큰 영역입니다.

종이에 그린 그림(테두리·배경)에 그림자를 칠하면 갑자기 떠 보이고, 한 장씩 빠르게 넘기면(키프레임) 움직이는 것처럼 보여요. CSS의 그림자·애니메이션이 바로 그 역할입니다.

같은 버튼이라도 효과 한 줌으로 인상이 완전히 달라집니다. 왼쪽은 효과 없는 기본 버튼, 오른쪽은 둥근 모서리·그림자·그라데이션·호버 전환을 준 버튼이에요(마우스를 올려보면 부드럽게 변합니다).

아래 데모는 진짜로 동작합니다

이 장의 데모는 실제 HTML? + <style> 을 그대로 실행해요. 마우스를 올리면(hover) 변하고, 회전·맥박 같은 애니메이션도 실제로 돕니다. 코드를 고치고 실행을 눌러 바로 결과를 확인하세요.

1. 테두리와 모서리 — border · border-radius · outline

무엇: 요소 둘레에 선을 두르고(테두리), 모서리를 둥글게 깎습니다(라운드).

border — 굵기 · 스타일 · 색

border 는 세 값을 한 번에 적는 단축 속성입니다: border: 굵기 스타일 색;. 스타일은 solid(실선) 외에도 여러 가지가 있어요.

.box {
  border: 2px solid #4f46e5;     /* 굵기 2px · 실선 · 색 */
}

/* 한 변만 따로 지정도 가능 */
.box { border-bottom: 3px dashed #dc2626; }

/* 스타일 종류 */
border-style: solid | dashed | dotted | double | groove | ridge | inset | outset | none;

border-radius — 둥근 · 원형 · 알약

border-radius 는 모서리를 깎는 반지름이에요. 값을 50% 로 주면 이 되고, 아주 큰 값(예 9999px)을 주면 양 끝이 둥근 알약(pill) 모양이 됩니다.

outline — 테두리 바깥의 외곽선

outline 은 테두리와 비슷하지만 레이아웃 자리를 차지하지 않습니다(요소를 밀어내지 않아요). 그래서 키보드 포커스 표시에 즐겨 씁니다. outline-offset 으로 요소와의 간격도 줄 수 있어요.

button:focus {
  outline: 2px solid #4f46e5;   /* 굵기 스타일 색 (border와 같은 순서) */
  outline-offset: 3px;          /* 요소에서 3px 떨어뜨려 그림 */
}
border 와 outline 차이

border 는 박스 크기에 포함돼 옆 요소를 밀어냅니다. outline 은 박스 위에 덧그려질 뿐이라 크기에 영향이 없어요. 접근성을 위해 포커스 외곽선은 지우지 말고 보기 좋게 다듬는 것을 권합니다.

2. 그림자 — box-shadow · text-shadow

무엇: 요소 뒤에 그림자를 깔아 떠 있는 느낌(입체감)을 줍니다. 카드·버튼·모달에서 가장 많이 쓰여요.

box-shadow — x y 흐림 번짐 색

값 순서는 box-shadow: x축 y축 흐림(blur) 번짐(spread) 색; 입니다. 맨 앞에 inset 을 붙이면 안쪽 그림자가 되고, 쉼표로 여러 겹을 겹칠 수 있어요.

/* x  y  blur spread color */
box-shadow: 0 4px 12px 0 rgba(0,0,0,.15);   /* 아래로 떨어지는 부드러운 그림자 */

box-shadow: inset 0 2px 6px rgba(0,0,0,.3);  /* 안쪽으로 파인 느낌 */

/* 여러 겹 — 쉼표로 나열 */
box-shadow: 0 1px 2px rgba(0,0,0,.1),
            0 8px 24px rgba(0,0,0,.15);

실제 카드와 버튼 그림자를 비교해 보세요. hover 시 그림자가 커지며 떠오르는 효과까지 동작합니다.

text-shadow — 글자 그림자

글자에 그림자를 줍니다. 순서는 text-shadow: x y 흐림 색; 으로 번짐(spread)·inset 은 없습니다. 가독성을 위해 밝은 배경 위 글자에 살짝 그림자를 주거나, 네온·엠보싱 효과에 씁니다.

3. 투명도와 필터 — opacity · filter

무엇: 요소를 흐리게·밝게·흑백으로 바꾸는 화면 효과입니다.

opacity — 통째로 투명하게

opacity0(완전 투명)부터 1(불투명)까지의 값으로, 요소를 내용까지 전부 투명하게 만듭니다. 색만 반투명하게 하고 싶다면 opacity 대신 rgba(...,.5) 처럼 색의 알파값을 쓰는 게 좋아요.

filter — blur · brightness · grayscale · drop-shadow

filter 는 이미지 보정 필터처럼 요소 전체에 효과를 겁니다. 여러 함수를 띄어쓰기로 나열하면 차례로 적용돼요.

함수효과
blur()흐리게(픽셀 단위)blur(4px)
brightness()밝기(1 = 원본, >1 밝게)brightness(1.3)
contrast()대비contrast(1.5)
grayscale()흑백(0~1 또는 0~100%)grayscale(1)
drop-shadow()모양을 따라가는 그림자drop-shadow(2px 4px 6px #000)

filter: drop-shadow()box-shadow 와 달리 박스가 아니라 내용물의 실제 모양(투명 PNG, 둥근 도형 등)을 따라 그림자를 그립니다. 단 번짐(spread) 값은 없어요.

4. 트랜지션 — transition

무엇: 어떤 속성이 바뀔 때 탁 변하지 않고 부드럽게 이어 주는 기능입니다. :hover 와 짝지으면 마우스를 올릴 때 색·크기가 스르륵 변해요.

순서는 transition: 속성 지속시간 가속곡선 지연; 입니다.

.btn {
  transition: background-color .3s ease, transform .3s ease;
  /*          속성             시간  곡선   (쉼표로 여러 속성)        */
}
.btn:hover { background-color:#4f46e5; transform: scale(1.05); }

/* 모든 속성을 한 번에 — all (편하지만 성능상 주의) */
transition: all .25s ease-out .1s;   /* 속성 시간 곡선 지연 */

가속 곡선(timing-function): linear(일정), ease(기본), ease-in(천천히 시작), ease-out(천천히 끝), ease-in-out, 직접 정의하는 cubic-bezier(...), 계단식 steps(...) 가 있어요.

5. 변형 — transform

무엇: 요소를 이동·확대·회전·기울이기 합니다. 레이아웃을 다시 계산하지 않고 화면에서만 변형해 아주 빠릅니다.

함수
translate(x, y)이동translate(10px, -5px)
scale(n)확대/축소scale(1.2) · scale(.8)
rotate(deg)회전(도 단위)rotate(15deg)
skew(deg)기울이기skew(10deg, 0)

여러 변형은 띄어쓰기로 나열해 한 번에 줍니다. transform-origin 으로 변형의 기준점(기본은 가운데)을 바꿀 수 있어요.

transform: translateY(-4px) rotate(8deg) scale(1.1);  /* 여러 변형 조합 */
transform-origin: top left;     /* 왼쪽 위를 기준으로 회전/확대 */

6. 애니메이션 — @keyframes + animation

무엇: 트랜지션이 "A→B 한 번"이라면, 애니메이션은 여러 단계를 정해 두고 반복할 수 있습니다. 먼저 @keyframes장면들을 정의하고, animation 으로 그 장면을 이름으로 불러 실행해요.

/* 1) 장면 정의: from(=0%) → to(=100%) 또는 % 단계 */
@keyframes spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@keyframes pulse {
  0%   { transform: scale(1);   opacity: 1; }
  50%  { transform: scale(1.15); opacity: .6; }
  100% { transform: scale(1);   opacity: 1; }
}

/* 2) 실행: 이름 시간 곡선 반복횟수 방향 */
.spinner { animation: spin 1s linear infinite; }
.dot     { animation: pulse 1.2s ease-in-out infinite; }

animation 단축 속성의 주요 값입니다.

이름(name)@keyframes 에서 정한 이름
시간(duration)한 바퀴에 걸리는 시간 1s
곡선(timing-function)linear · ease-in-out
반복(iteration-count)횟수 또는 infinite(무한)
방향(direction)normal · reverse · alternate(왕복)

회전 스피너, 맥박(pulse), 페이드인을 한꺼번에 실행해 봅니다.

7. 커서와 선택 — cursor · user-select

cursor 는 마우스를 올렸을 때의 커서 모양을 바꿉니다. 버튼엔 pointer(손가락), 비활성엔 not-allowed 등.

user-select 는 사용자가 드래그로 글자를 선택할 수 있는지 정합니다. 버튼 라벨처럼 선택될 필요 없는 텍스트엔 none 을 줘요.

.btn       { cursor: pointer; }       /* 손가락 */
.disabled  { cursor: not-allowed; }   /* 금지 */
.draggable { cursor: grab; }          /* 잡기 */

.label { user-select: none; }   /* 드래그 선택 막기 (auto | text | all | none) */

한눈에 — 효과/애니메이션 카탈로그

속성용도값 순서 / 주요 값
border테두리굵기 스타일 색 (2px solid #333)
border-radius둥근 모서리길이/% (50%=원, 큰 값=알약)
outline바깥 외곽선(자리 안 차지)굵기 스타일 색 + outline-offset
box-shadow박스 그림자x y blur spread color, inset, 쉼표로 여러 겹
text-shadow글자 그림자x y blur color (spread·inset 없음)
opacity전체 투명도0~1
filter화면 효과blur · brightness · grayscale · drop-shadow (띄어쓰기로 합성)
transition속성 변화 부드럽게속성 시간 곡선 지연
transform이동·확대·회전·기울이기translate · scale · rotate · skew (띄어쓰기로 조합)
transform-origin변형 기준점기본 center (top left 등)
@keyframes애니메이션 장면 정의from/to 또는 0%~100%
animation키프레임 실행이름 시간 곡선 반복(infinite) 방향(alternate)
cursor마우스 커서 모양pointer · grab · not-allowed · text
user-select드래그 선택 허용auto · text · all · none
실무 메모 — 성능과 접근성

부드러운 애니메이션은 transformopacity 위주로. 이 둘은 브라우저가 GPU로 처리해 가장 매끄럽습니다. 반면 width·top·margin 같은 값을 애니메이션하면 매 프레임마다 레이아웃을 다시 계산해 끊길 수 있어요. 위치를 옮길 땐 top 대신 transform: translate() 를 쓰세요.

또한 일부 사용자는 움직임에 어지럼을 느낍니다. prefers-reduced-motion 미디어 쿼리로 "움직임 줄이기" 설정을 켠 사용자에겐 애니메이션을 멈추거나 약하게 해 주세요.

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

다음 단계