[퍼블 복기 시리즈]다양한 버튼을 보러오세요.

nana·2026년 7월 15일

퍼블리셔 복기

목록 보기
1/5
post-thumbnail

다양한 버튼이 필요한 당신

나는 이제 퍼블리셔가 사용할 수 있는 모든 컴포넌트를 제작하기로 마음 먹었다. 진짜다. 진짜... 할거다~ 이것은 버튼 시리즈다.

일단 처음 작업은 가장 친숙한 HTML과 CSS, JS로 시작하겠다.

코드펜 작업물

Day 1 · 호버 버튼 갤러리 (12종)

CodePen용 코드. 버튼 목록이라 <ul> + <li>로 구성하고, 4열로 정렬했다.
접근성 요소(포커스 표시, 색 비의존 라벨, 모션 최소화) 포함.


HTML

<main class="wrap">
  <h1 class="sr-only">호버 버튼 효과 갤러리</h1>

  <ul class="btn-list">
    <li>
      <button class="btn b1">Hover</button>
      <span class="label">색 + 확대 <em>fill scale</em></span>
    </li>
    <li>
      <button class="btn b2">Hover</button>
      <span class="label">색 반전 <em>outline</em></span>
    </li>
    <li>
      <button class="btn b3">Hover</button>
      <span class="label">떠오름 <em>lift</em></span>
    </li>
    <li>
      <button class="btn b4"><span class="txt">Hover</span></button>
      <span class="label">슬라이드 채움 <em>slide</em></span>
    </li>
    <li>
      <button class="btn b5"><span class="txt">Hover</span></button>
      <span class="label">물결 <em>ripple</em></span>
    </li>
    <li>
      <button class="btn b6">Hover</button>
      <span class="label">글로우 <em>glow</em></span>
    </li>
    <li>
      <button class="btn b7">Hover</button>
      <span class="label">그라데이션 <em>gradient</em></span>
    </li>
    <li>
      <button class="btn b8">Hover</button>
      <span class="label">자간 확장 <em>spacing</em></span>
    </li>
    <li>
      <button class="btn b9">Hover</button>
      <span class="label">눌림 <em>press</em></span>
    </li>
    <li>
      <button class="btn b10">Hover</button>
      <span class="label">모서리 변형 <em>radius</em></span>
    </li>
    <li>
      <button class="btn b11">Hover <span class="arrow" aria-hidden="true"></span></button>
      <span class="label">아이콘 이동 <em>icon shift</em></span>
    </li>
    <li>
      <button class="btn b12" id="magnetic">Hover</button>
      <span class="label">자석 <em>magnetic</em></span>
    </li>
  </ul>
</main>

CSS

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff3ed;
  font-family: system-ui, sans-serif;
}

/* 스크린리더 전용 텍스트 */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

.wrap { max-width: 720px; padding: 32px; }

.btn-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);  /* 4열 고정 */
  gap: 28px 18px;
}

/* 모바일: 2열 */
@media (max-width: 600px) {
  .btn-list { grid-template-columns: repeat(2, 1fr); }
}

.btn-list li {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.label {
  font-size: 13px;
  font-weight: 600;
  color: #4a3f6b;
  text-align: center;
  line-height: 1.4;
}

.label em {
  display: block;
  font-size: 11px;
  font-weight: 400;
  font-style: normal;
  color: #8a7fb0;
}

.btn {
  padding: 14px 26px;
  font-size: 15px;
  font-weight: 600;
  border: none;
  border-radius: 12px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  font-family: inherit;
}

.btn:focus-visible {
  outline: 3px solid #2d3436;
  outline-offset: 3px;
}

/* ① 색 + 확대 */
.b1 { background: #6c5ce7; color: #fff; transition: all .25s ease; }
.b1:hover { background: #4834b4; transform: scale(1.06); }

/* ② 색 반전 */
.b2 { background: #fff; color: #4834b4; border: 2px solid #4834b4; transition: all .25s ease; }
.b2:hover { background: #4834b4; color: #fff; }

/* ③ 떠오름 */
.b3 { background: #fff; color: #333; box-shadow: 0 2px 6px rgba(0,0,0,.12); transition: all .25s ease; }
.b3:hover { transform: translateY(-4px); box-shadow: 0 10px 22px rgba(0,0,0,.18); }

/* ④ 슬라이드 채움 */
.b4 { background: #008068; color: #fff; transition: all .3s ease; }
.b4::before { content: ""; position: absolute; inset: 0; background: #005c4a; transform: translateX(-100%); transition: transform .3s ease; z-index: 0; }
.b4:hover::before { transform: translateX(0); }
.b4 .txt { position: relative; z-index: 1; }

/* ⑤ 물결 */
.b5 { background: #c0492a; color: #fff; transition: color .3s; }
.b5::before { content: ""; position: absolute; left: 50%; top: 50%; width: 0; height: 0; background: #8a2f19; border-radius: 50%; transform: translate(-50%,-50%); transition: width .4s ease, height .4s ease; z-index: 0; }
.b5:hover::before { width: 250%; height: 250%; }
.b5 .txt { position: relative; z-index: 1; }

/* ⑥ 글로우 */
.b6 { background: #0069b3; color: #fff; transition: box-shadow .3s ease; }
.b6:hover { box-shadow: 0 0 18px 2px #4da3e0; }

/* ⑦ 그라데이션 흐름 */
.b7 { color: #fff; background: linear-gradient(90deg, #6c5ce7, #d6467f, #6c5ce7); background-size: 200% 100%; background-position: 0 0; transition: background-position .5s ease; }
.b7:hover { background-position: 100% 0; }

/* ⑧ 자간 확장 */
.b8 { background: #2d3436; color: #fff; transition: letter-spacing .25s ease, background .25s; }
.b8:hover { letter-spacing: 3px; background: #6c5ce7; }

/* ⑨ 눌림 */
.b9 { background: #008068; color: #fff; transition: transform .2s; }
.b9:hover { transform: translateY(-2px); }
.b9:active { transform: scale(.92); }

/* ⑩ 모서리 변형 */
.b10 { background: #fff; color: #b02a6b; border: 2px solid #b02a6b; border-radius: 30px; transition: all .3s ease; }
.b10:hover { border-radius: 10px; background: #b02a6b; color: #fff; }

/* ⑪ 아이콘 이동 */
.b11 { background: #9a6a08; color: #fff; }
.b11 .arrow { display: inline-block; transition: transform .3s ease; }
.b11:hover .arrow { transform: translateX(5px); }

/* ⑫ 자석 */
.b12 { background: #555e62; color: #fff; transition: transform .15s ease; }

/* 모션 최소화 배려 */
@media (prefers-reduced-motion: reduce) {
  .btn, .btn::before, .b11 .arrow { transition: none !important; }
}

JS (자석 버튼)

const magnetic = document.getElementById("magnetic");

magnetic.addEventListener("mousemove", (e) => {
  const rect = magnetic.getBoundingClientRect();
  const x = e.clientX - rect.left - rect.width / 2;
  const y = e.clientY - rect.top - rect.height / 2;
  magnetic.style.transform = `translate(${x * 0.4}px, ${y * 0.4}px)`;
});

magnetic.addEventListener("mouseleave", () => {
  magnetic.style.transform = "translate(0, 0)";
});

각 버튼 핵심 정리

#이름핵심 기술
1색 + 확대transition + transform: scale()
2색 반전:hover로 배경/글자색 교체
3떠오름translateY + box-shadow
4슬라이드 채움::before + translateX
5물결::before 원형 확대
6글로우box-shadow 번짐
7그라데이션 흐름background-position 이동
8자간 확장letter-spacing 전환
9눌림:active + scale
10모서리 변형border-radius 전환
11아이콘 이동자식만 translateX
12자석JS로 마우스 위치 추적

실험 포인트

  • transition 시간: .25s.6s로 느린 느낌 체감
  • easing: easecubic-bezier(.68,-0.55,.27,1.55) 넣으면 통통 튀는 느낌
  • 자석 강도: JS의 0.40.8로 올리면 더 강하게 끌려옴
  • ::before 트릭: ④⑤번은 가짜 요소를 깔고 overflow: hidden으로 잘라내는 패턴 — 응용 범위가 넓다

기록용 한 줄

  • 뭘 만들었나: 호버 버튼 12종 갤러리 (ul/li, 4열)
  • 어디서 막혔나: ::before가 글자를 덮어서 안 보임 → .txtposition: relative; z-index: 1로 해결
  • 뭘 알게 됐나: overflow: hidden + ::before 조합이 슬라이드·물결 효과의 핵심
profile
안녕하세요 프론트엔드&퍼블리셔 nana의 블로그입니다.

0개의 댓글