이번 편의 결과물: 프로젝트 카드의 정보 아이콘을 hover·포커스하면 그 아이콘 위치를 기준으로 툴팁이 뜨고, 화면 아래쪽 카드에서는 자동으로 반대편에 뜹니다. anchor-name을 지원하지 않는 브라우저에서는 카드 안쪽에 절대 위치로 대체됩니다. 다루는 개념: anchor-name/position-anchor, anchor() 함수, position-try-fallbacks, @supports 폴백
이 편에서 만드는 파일
web-practice/portfolio/
├── projects.html ~ (카드마다 정보 아이콘 버튼 + 툴팁 요소 추가)
└── css/
└── styles.css ~ (components 레이어에 툴팁 스타일 추가)
web-practice/lessons/modern-css/
└── 17_anchor-positioning-tooltip/
└── notes.md + (앵커 포지셔닝 확인 메모)개념 정리
앵커 포지셔닝은 한 요소(툴팁)를 다른 요소(트리거)의 화면 좌표에 붙이는 CSS 기능입니다. 지금까지는 JavaScript로 트리거의 getBoundingClientRect()를 읽어 툴팁 좌표를 계산했지만, 앵커 포지셔닝은 이 계산을 CSS만으로 처리합니다.
| 속성/함수 | 역할 |
|---|---|
anchor-name: --name | 트리거 요소에 이름을 붙여 “앵커”로 등록 |
position-anchor: --name | position: absolute/fixed 요소를 그 앵커에 연결 |
anchor(side) | 앵커의 특정 변(top/bottom/center 등) 좌표를 가져옴. position-anchor가 있으면 이름 생략 가능 |
position-try-fallbacks: flip-block | 화면 밖으로 넘치면 반대 방향으로 자동 뒤집기 |
position-anchor가 지정된 요소 안에서는 anchor()에 이름을 생략할 수 있습니다. top: anchor(bottom)은 “내가 연결된 앵커의 아래쪽 변”을 뜻합니다.
anchor-name은 페이지 안에서 커스텀 식별자(custom ident)라 카드 6개처럼 같은 컴포넌트가 여러 번 반복되면 이름이 겹치지 않게 카드마다 다른 이름을 줘야 합니다. 01_학습방향.md 확인표 기준 앵커 포지셔닝은 2026-01부터 Baseline 2026(newly available)이라, @supports not (anchor-name: --a)로 미지원 브라우저용 대체 위치를 반드시 마련합니다.
실습
1. projects.html에 정보 아이콘과 툴팁 추가하기
카드마다 .info-trigger 버튼과 그 바로 다음 형제로 .card-tooltip을 넣습니다. 형제 관계라야 다음 단계의 :hover + , :focus-visible + 선택자가 동작합니다.
<!-- portfolio/projects.html (카드 1개 예시, 나머지 5개도 같은 구조) -->
<article class="project-card" data-tags="JavaScript,PWA">
<img src="images/project-01.svg" alt="오늘의 날씨 프로젝트 화면" width="1200" height="800" />
<h2>오늘의 날씨</h2>
<p>
위치 기반 날씨를 카드로 보여주는 웹앱입니다.
<button type="button" class="info-trigger" aria-describedby="tooltip-01">ⓘ</button>
<span role="tooltip" id="tooltip-01" class="card-tooltip">
오프라인에서도 마지막으로 받은 데이터를 Service Worker 캐시에서 보여줍니다.
</span>
</p>
<ul class="tags">
<li>JavaScript</li>
<li>PWA</li>
</ul>
<button type="button" data-open-project data-project-id="weather">자세히</button>
</article>2. 앵커 포지셔닝 스타일 작성하기
카드마다 anchor-name이 겹치지 않도록 :nth-of-type()으로 카드 순서별 이름을 붙입니다. 공통 툴팁 모양과 표시 조건은 한 번만 씁니다.
/* portfolio/css/styles.css — components 레이어에 추가 */
@layer components {
.project-card:nth-of-type(1) .info-trigger {
anchor-name: --info-1;
}
.project-card:nth-of-type(2) .info-trigger {
anchor-name: --info-2;
}
.project-card:nth-of-type(3) .info-trigger {
anchor-name: --info-3;
}
.project-card:nth-of-type(4) .info-trigger {
anchor-name: --info-4;
}
.project-card:nth-of-type(5) .info-trigger {
anchor-name: --info-5;
}
.project-card:nth-of-type(6) .info-trigger {
anchor-name: --info-6;
}
.project-card:nth-of-type(1) .card-tooltip {
position-anchor: --info-1;
}
.project-card:nth-of-type(2) .card-tooltip {
position-anchor: --info-2;
}
.project-card:nth-of-type(3) .card-tooltip {
position-anchor: --info-3;
}
.project-card:nth-of-type(4) .card-tooltip {
position-anchor: --info-4;
}
.project-card:nth-of-type(5) .card-tooltip {
position-anchor: --info-5;
}
.project-card:nth-of-type(6) .card-tooltip {
position-anchor: --info-6;
}
.info-trigger {
display: inline-grid;
place-items: center;
inline-size: 1.25rem;
block-size: 1.25rem;
border-radius: 50%;
border: 1px solid var(--color-border);
background: var(--color-surface);
color: var(--color-text-muted);
font-size: var(--text-xs);
cursor: help;
}
.card-tooltip {
position: fixed;
top: calc(anchor(bottom) + var(--space-2));
left: anchor(center);
translate: -50% 0;
position-try-fallbacks: flip-block;
inline-size: max-content;
max-inline-size: 14rem;
padding: var(--space-2) var(--space-3);
border-radius: var(--radius);
background: var(--color-text);
color: var(--color-bg);
font-size: var(--text-xs);
line-height: var(--leading);
opacity: 0;
visibility: hidden;
transition: opacity 150ms ease;
}
.info-trigger:hover + .card-tooltip,
.info-trigger:focus-visible + .card-tooltip {
opacity: 1;
visibility: visible;
}
/* anchor-name 미지원 브라우저 폴백 — 카드 안쪽 상대 위치로 대체 */
@supports not (anchor-name: --info-1) {
.project-card {
position: relative;
}
.card-tooltip {
position: absolute;
top: auto;
bottom: calc(100% + var(--space-2));
left: var(--space-4);
translate: none;
}
}
}3. 실행
VS Code에서 portfolio/projects.html을 브라우저로 엽니다(라이브 서버 확장 권장).4. 확인
- 카드 설명 옆
ⓘ아이콘에 마우스를 올리면 아이콘 바로 아래에 검정 배경 툴팁이 뜬다 - 키보드로
Tab을 눌러 아이콘에 포커스를 옮겨도 같은 툴팁이 뜬다(마우스 없이도 확인 가능) - 브라우저 개발자 도구에서
styles.css의anchor-name줄에 취소선이 그어져 있으면(미지원 표시) 툴팁이 카드 안쪽 왼쪽 위에 절대 위치로 뜨는 것을 확인한다 - 화면 아래쪽 카드(5번, 6번)에서도 툴팁이 화면 밖으로 넘치지 않고 아이콘 근처에 붙어 있다
직접 해보기
position-try-fallbacks: flip-block을 지우고 마지막 줄 카드의 툴팁이 화면 아래로 잘리는지 확인해 보세요. 다시 넣으면 어떻게 달라지나요..card-tooltip의left: anchor(center)를left: anchor(start)로 바꾸면 툴팁이 아이콘의 어느 지점에 맞춰지는지 관찰해 보세요.
답 보기
flip-block이 없으면 뷰포트 아래쪽 카드의 툴팁이top: anchor(bottom)규칙 그대로 화면 밑으로 밀려나 잘립니다.flip-block을 넣으면 공간이 부족할 때 자동으로 앵커 위쪽에 뜨도록 뒤집힙니다.anchor(center)는 아이콘의 가로 중앙에 맞추고,anchor(start)는 아이콘의 왼쪽 변(inline-start)에 맞춥니다.translate: -50% 0과 짝을 이루는 것은center뿐이므로,start로 바꾸면translate값도 함께 조정해야 툴팁이 어긋나지 않습니다.
자주 하는 실수
| 증상 | 원인 | 고치는 법 |
|---|---|---|
| 툴팁이 항상 화면 왼쪽 위(0, 0)에 붙는다 | position-anchor가 연결한 anchor-name을 가진 요소가 없다(이름 오타) | 트리거의 anchor-name과 툴팁의 position-anchor 값이 정확히 같은 문자열인지 확인 |
| 6개 카드 툴팁이 전부 1번 카드 위치에 뜬다 | 모든 카드에 같은 anchor-name을 재사용했다 | 카드마다 --info-1부터 --info-6까지 고유한 이름을 부여 |
| 미지원 브라우저에서 툴팁이 안 보인다 | @supports not (anchor-name: --info-1) 안 폴백에서 position: relative를 부모(.project-card)에 걸지 않았다 | 폴백 블록에서 기준 요소에 position: relative를 반드시 함께 지정 |
| 툴팁 텍스트가 카드 폭만큼 넓게 퍼진다 | .card-tooltip이 inline-size: max-content가 아니라 부모 폭을 상속받는 다른 규칙과 충돌 | max-inline-size로 상한을 주고 inline-size: max-content를 유지 |