CSS Anchor Positioning: JavaScript 없이 툴팁과 팝오버 만들기

프론트엔드

CSSAnchor PositioningPopover API툴팁CSS 신기능

이 글은 누구를 위한 것인가

  • Popper.js, Floating UI 같은 JS 라이브러리를 CSS로 대체하고 싶은 팀
  • 복잡한 툴팁 위치 계산 로직을 제거하고 싶은 개발자
  • CSS 최신 기능으로 뷰포트 인식 팝오버를 구현하려는 프론트엔드 개발자

들어가며

툴팁 하나를 만드는데 Popper.js 같은 무거운 라이브러리가 필요했다. CSS Anchor Positioning은 이 문제를 CSS만으로 해결한다. 앵커 요소 기준 위치 지정, 뷰포트 벗어남 자동 폴백까지 네이티브로 지원한다.

이 글은 bluefoxdev.kr의 CSS 신기능 실전 가이드 를 참고하여 작성했습니다.


1. Anchor Positioning 핵심 개념

[CSS Anchor Positioning 구조]

앵커 요소: anchor-name 속성으로 이름 지정
위치 요소: position-anchor로 앵커 참조

기본 흐름:
  1. 앵커에 이름 부여: anchor-name: --my-button
  2. 팝업에 앵커 참조: position-anchor: --my-button
  3. 팝업 위치 지정: position-area: top  (또는 anchor() 함수)

[position-area 그리드]
  9칸 그리드로 위치 지정:
  top start   | top center    | top end
  center start| center center | center end
  bottom start| bottom center | bottom end

[anchor() 함수]
  top: anchor(bottom)    → 앵커 아래에 배치
  left: anchor(right)    → 앵커 오른쪽에 배치
  정밀한 오프셋 계산 가능

[@position-try]
  뷰포트 벗어날 때 대체 위치 자동 시도
  Popper.js의 flip 기능을 CSS로 구현

2. CSS Anchor Positioning 구현

/* 기본 툴팁 */
.button {
  anchor-name: --tooltip-anchor;
  position: relative;
}

.tooltip {
  position: absolute;
  position-anchor: --tooltip-anchor;
  
  /* 앵커 위쪽 중앙 배치 */
  position-area: top center;
  
  /* 앵커에서 8px 떨어지게 */
  margin-bottom: 8px;
  
  /* 스타일 */
  background: #1a1a2e;
  color: white;
  padding: 6px 12px;
  border-radius: 6px;
  font-size: 14px;
  white-space: nowrap;
  
  /* 기본적으로 숨김 */
  opacity: 0;
  transition: opacity 0.2s;
}

/* 버튼 호버 시 툴팁 표시 */
.button:hover + .tooltip,
.button:focus + .tooltip {
  opacity: 1;
}

/* 뷰포트 벗어남 자동 처리 */
@position-try --flip-bottom {
  position-area: bottom center;
  margin-top: 8px;
  margin-bottom: 0;
}

@position-try --flip-right {
  position-area: right center;
  margin-left: 8px;
  margin-bottom: 0;
}

.tooltip {
  position-try-fallbacks: --flip-bottom, --flip-right;
}

/* 툴팁 화살표 */
.tooltip::before {
  content: '';
  position: absolute;
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: #1a1a2e;
  border-bottom: none;
}
/* Popover API + Anchor Positioning 결합 */

/* 더보기 메뉴 버튼 */
.menu-trigger {
  anchor-name: --menu-anchor;
}

/* Popover (HTML popover 속성 필수) */
.dropdown-menu {
  position: absolute;
  position-anchor: --menu-anchor;
  position-area: bottom end;
  margin-top: 4px;
  
  /* Popover 기본 스타일 초기화 */
  border: none;
  padding: 0;
  
  background: white;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.1);
  min-width: 160px;
  
  /* Popover 표시/숨김 애니메이션 */
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.15s, transform 0.15s, display 0.15s allow-discrete;
}

.dropdown-menu:popover-open {
  opacity: 1;
  transform: translateY(0);
}

@starting-style {
  .dropdown-menu:popover-open {
    opacity: 0;
    transform: translateY(-4px);
  }
}

/* 뷰포트 오른쪽 끝 처리 */
@position-try --align-left {
  position-area: bottom start;
}

.dropdown-menu {
  position-try-fallbacks: --align-left;
}
<!-- HTML 구조 -->
<button
  class="menu-trigger"
  popovertarget="user-menu"
>
  내 계정 ▾
</button>

<menu
  id="user-menu"
  class="dropdown-menu"
  popover
>
  <li><a href="/profile">프로필</a></li>
  <li><a href="/settings">설정</a></li>
  <li>
    <button popovertarget="user-menu" popovertargetaction="hide">
      로그아웃
    </button>
  </li>
</menu>

<!-- 툴팁 -->
<button class="button" type="button">
  저장
</button>
<div class="tooltip" role="tooltip">
  Ctrl+S로도 저장할 수 있습니다
</div>

<!-- 고급: aria 연결 -->
<button
  class="button"
  type="button"
  aria-describedby="save-tooltip"
>
  저장
</button>
<div id="save-tooltip" class="tooltip" role="tooltip">
  변경사항이 자동 저장됩니다
</div>
/* position-area 전체 옵션 시각화 */

/* 수직 배치 */
.popup { position-area: top; }      /* 위 */
.popup { position-area: bottom; }   /* 아래 */

/* 수평 배치 */
.popup { position-area: left; }     /* 왼쪽 */
.popup { position-area: right; }    /* 오른쪽 */

/* 모서리 배치 */
.popup { position-area: top left; }
.popup { position-area: top right; }
.popup { position-area: bottom left; }
.popup { position-area: bottom right; }

/* 정렬 조합 */
.popup { position-area: top span-all; }  /* 위쪽, 앵커 전체 너비 */
.popup { position-area: bottom center; }  /* 아래쪽 중앙 */

/* anchor() 함수 (세밀한 제어) */
.popup {
  top: anchor(bottom);   /* 앵커 하단에 붙임 */
  left: anchor(center);  /* 앵커 중앙 정렬 */
  transform: translateX(-50%);
}

/* 브라우저 지원 확인 */
@supports (anchor-name: --test) {
  /* Anchor Positioning 지원 */
  .tooltip { position: absolute; }
}

@supports not (anchor-name: --test) {
  /* 폴백: JavaScript 기반 툴팁 */
  .tooltip { display: none; }
}

마무리

CSS Anchor Positioning은 Popper.js, Floating UI의 JS 의존성을 제거할 수 있는 강력한 대안이다. position-area로 9방향 배치, @position-try로 뷰포트 인식 폴백, Popover API와 결합하면 JS 없이 접근성 높은 드롭다운/툴팁을 만들 수 있다. 2026년 기준 Chrome, Edge, Firefox 최신 버전에서 지원되며, Safari도 지원이 추가됐다. @supports로 점진적 향상을 적용하면 구형 브라우저에서도 안전하다.