컴포넌트를 설계하고 화면에 배치하다 보면 "아, 이 툴팁은 무조건 이 버튼 딱 옆에 붙어있어야 하는데!" 하거나 "드롭다운 메뉴가 화면 밖으로 잘리지 않게 알아서 위치를 바꿨으면 좋겠다"라는 생각이 드실 때가 많을 거예요.
오늘 우리가 함께 살펴볼 MDN 문서는 바로 그 고민을 말끔히 해결해 줄 'CSS 앵커 포지셔닝(CSS anchor positioning) 사용하기'입니다. 예전에는 자바스크립트로 복잡하게 좌표를 계산해서 요소들을 따라다니게 만들었다면, 이제는 순수 CSS만으로 요소를 마치 '닻(Anchor)'에 묶어둔 것처럼 연결할 수 있습니다. 컴포넌트 주도 개발(CDD)을 할 때 UI 요소 간의 결합도를 낮추고 성능을 끌어올릴 수 있는 아주 강력한 무기랍니다.
CSS 앵커 포지셔닝(CSS anchor positioning) 모듈은 요소들을 서로 묶어서(tether) 연결할 수 있게 해주는 기능들을 정의합니다. 특정 요소를 앵커 요소(anchor elements)로 정의하고, 다른 요소를 앵커에 위치된 요소(anchor-positioned elements)로 정의할 수 있죠. 앵커에 위치된 요소들은 앵커 요소에 묶일(bound) 수 있습니다. 이렇게 묶인 요소는 자신이 연결된 앵커 요소의 크기와 위치를 기준으로 자신의 크기와 위치를 설정할 수 있게 됩니다.
CSS 앵커 포지셔닝은 또한 앵커에 위치된 요소에게 여러 개의 대안적인 위치를 지정할 수 있는 CSS 전용 메커니즘을 제공합니다. 예를 들어, 툴팁이 폼(form) 필드에 앵커로 연결되어 있는데 기본 위치 설정대로라면 툴팁이 화면 밖으로 벗어나 잘리게 되는 상황을 생각해 보세요. 브라우저는 툴팁이 화면 안에 잘 보이도록 미리 제안된 다른 위치에 렌더링을 시도하거나, 원한다면 아예 숨겨버릴 수도 있습니다.
이 문서에서는 앵커 포지셔닝의 핵심 개념들을 설명하고, 이 모듈이 제공하는 연결(association), 위치 지정(positioning), 크기 지정(sizing) 기능들을 아주 기초적인 수준에서 사용하는 방법을 다룹니다. 아래에서 논의되는 각 개념들에 대해 더 많은 예제와 구문(syntax) 세부 정보를 확인할 수 있는 참조 페이지 링크도 포함해 두었습니다. 대안 위치를 지정하거나 앵커에 위치된 요소를 숨기는 방법에 대한 정보는 오버플로우를 위한 폴백 옵션 및 조건부 숨김(Fallback options and conditional hiding for overflow) 가이드를 참고하세요.
👨🏫 부연 설명:
앵커 포지셔닝은 2024년 기준 브라우저에 갓 도입되기 시작한 초신성 같은 기능입니다. 지금까지는 버튼 옆에 팝업을 띄우려면 React나 Next.js 환경에서getBoundingClientRect()같은 자바스크립트 API를 이용해 버튼의 좌표를 찾고 상태(State)로 업데이트해서 팝업의top,left값을 줘야 했죠. 하지만 이 모듈을 쓰면 자바스크립트 연산 없이 CSS만으로 성능 최적화까지 챙길 수 있습니다!
anchor-center를 사용하여 앵커의 중앙에 맞추기 (Centering on the anchor using anchor-center)anchor-size()의 다른 쓰임새 (Other uses of anchor-size())하나의 요소를 다른 요소에 묶거나(tether) 결합(bind)하고 싶어 하는 건 개발을 하다 보면 아주 흔하게 겪는 일입니다. 몇 가지 예를 들어볼까요?
현대의 인터페이스는 종종 어떤 콘텐츠(보통 재사용 가능하고 동적으로 생성되는 콘텐츠)가 기준이 되는 앵커 요소의 위치에 비례하여 놓여야 할 것을 요구합니다. 만약 기준이 될 요소(일명 앵커 요소)가 항상 UI 상의 똑같은 자리에만 있고, 묶일 요소(일명 앵커에 위치된 요소 또는 그냥 위치된 요소)가 항상 소스 코드 상에서 앵커 바로 앞이나 뒤에만 존재한다면, 이런 사용 사례를 구현하는 건 꽤 간단할 겁니다. 하지만 실무 환경은 그렇게 단순한 경우가 거의 없죠.
앵커 요소가 움직이거나 설정이 변경될 때(예: 스크롤링, 뷰포트 크기 변경, 드래그 앤 드롭 등)마다 앵커 요소에 대한 위치된 요소의 상대적인 좌표는 계속 유지되고 조정되어야 합니다. 예를 들어, 폼 필드 같은 요소가 뷰포트(화면) 가장자리에 너무 가까워지면 그 툴팁은 화면 밖으로 벗어나 안 보일 수 있습니다. 일반적으로 우리는 툴팁을 폼 컨트롤에 묶어두고, 폼 필드가 화면에 보이는 한 툴팁 역시 화면에 온전히 잘 보이도록 필요에 따라 툴팁 위치를 자동으로 이동시키길 원합니다. 컴퓨터 바탕화면이나 랩톱에서 우클릭(Ctrl + 클릭) 컨텍스트 메뉴를 띄웠을 때 알아서 잘리지 않는 방향으로 열리는 기본 동작을 보신 적이 있을 텐데, 바로 그것과 같습니다.
역사적으로, 한 요소를 다른 요소에 연결하고 앵커의 움직임에 따라 위치된 요소의 위치와 크기를 동적으로 변경하는 작업은 자바스크립트(JavaScript)를 필요로 했습니다. 이는 코드의 복잡성을 높이고 성능 문제를 야기했죠. 게다가 모든 상황에서 완벽하게 작동한다는 보장도 없었습니다. CSS 앵커 포지셔닝(CSS anchor positioning) 모듈에 정의된 기능들은 자바스크립트 대신 CSS(그리고 HTML)를 사용해 선언적(declaratively)으로, 그리고 높은 성능으로 이러한 사례들을 구현할 수 있게 해줍니다.
💡 강사 팁:
화면에 그리는 요소가 많아질수록 자바스크립트로 레이아웃(Layout) 연산을 매번 다시 하는 건 렌더링 최적화에 큰 독이 됩니다. 브라우저 엔진 자체가 처리하게끔 CSS로 위임(Delegate)하는 것은 최신 프론트엔드 트렌드의 핵심 철학 중 하나예요!
한 요소를 앵커에 연결하려면, 가장 먼저 어떤 요소가 '앵커'인지 선언하고, 그다음에 어떤 요소(들)를 그 앵커에 연결할 것인지 지정해야 합니다. 이렇게 하면 두 요소 간에 '앵커 참조(anchor reference)'가 형성되죠. 이 연결은 CSS를 통해 명시적으로 만들 수도 있고, 암시적으로 생성될 수도 있습니다.
어떤 요소를 앵커로 선언하기 위해 CSS를 사용한다면, anchor-name 속성을 통해 그 요소에 앵커 이름을 부여해야 합니다. 이 앵커 이름은 반드시 <dashed-ident> (대시 두 개 --로 시작하는 식별자) 형태여야 합니다. 아래 예제에서는 앵커 효과를 더 잘 보여주기 위해 자그마한 정사각형 앵커를 만들고자 앵커의 width를 fit-content로 설정했습니다.
.anchor {
anchor-name: --my-anchor;
width: fit-content;
}
이제 특정 요소를 '앵커에 위치된 요소(anchor-positioned element)'로 전환하려면 두 가지 단계가 필요합니다.
첫째, position 속성을 사용해 absolute 또는 fixed로 위치(positioned)를 지정해야 합니다.
둘째, 이 위치된 요소의 position-anchor 속성값으로 아까 만들어둔 앵커 요소의 anchor-name 값을 설정하여 두 요소를 서로 묶어줍니다.
.infobox {
position: fixed;
position-anchor: --my-anchor;
}
위의 CSS를 다음 HTML에 적용해 볼까요?
<div class="anchor">⚓︎</div>
<div class="infobox">
<p>This is an information box.</p>
</div>

💡 강사 팁:
--로 시작하는 작명 규칙, 어디서 많이 본 것 같지 않나요? 맞습니다. CSS 사용자 지정 속성(CSS 변수)을 만들 때 쓰는 그 규칙이에요. 덕분에 코드 충돌을 막고 나만의 고유한 앵커 이름을 선언할 수 있습니다.
이제 앵커와 정보 박스(infobox)는 성공적으로 연결되었습니다. 하지만 지금 당장 렌더링된 화면에서는 아직 서로에게 딱 들러붙어(tethered) 있지는 않은 상태입니다. 만약 앵커의 위치를 페이지의 다른 곳으로 이동시키더라도, 정보 박스는 제자리에 가만히 있고 앵커만 혼자 움직일 거예요. 이 둘이 실제로 착! 달라붙어서 함께 움직이는 모습은 이어지는 앵커를 기준으로 요소 위치 지정하기 섹션에서 확인하실 수 있습니다.
어떤 경우에는 요소들이 가진 의미론적인(semantic) 관계의 특성 때문에 두 요소 간에 암시적인 앵커 참조가 만들어지기도 합니다.
popovertarget과 id 속성, 또는 commandfor와 id 속성을 사용해 선언적으로 팝오버와 컨트롤을 연결할 때.source 옵션을 사용하여 showPopover() 같은 팝오버 동작을 프로그래밍 방식(자바스크립트)으로 컨트롤과 연결할 때.<select> 요소와 그 안에 든 드롭다운 피커(picker)가 appearance 속성의 base-select 값을 통해 커스터마이징 가능한 셀렉트 요소(customizable select element) 기능을 사용하도록 설정되었을 때. 이 경우 두 요소 사이에 암시적인 팝오버-호출자 관계가 생성되며, 이는 곧 암시적 앵커 참조를 갖게 됨을 의미합니다.참고:
위에서 설명한 방법들은 앵커를 요소와 연결해 주긴 하지만, 아직 완전히 '묶여있는(tethered)' 상태는 아닙니다. 이들을 서로 딱 붙여서 묶으려면, 위치된 요소가 CSS를 사용하여 앵커를 기준으로 배치되도록 설정해야 합니다.
만약 앵커 요소와 위치된 요소 사이에 이전에 명시적으로 맺어두었던 앵커 연결을 제거하고 싶다면, 다음 중 하나의 방법을 쓸 수 있습니다.
anchor-name 속성값을 none으로 설정합니다. 만약 다른 요소가 이 앵커에 물리게 하고 싶다면 다른 <dashed-ident> 값으로 바꿔주면 됩니다.position-anchor 속성값을 none으로 설정하거나, 현재 문서(document)에 존재하지 않는 앵커 이름(예: --not-an-anchor-name)으로 설정합니다.하지만 암시적인 앵커 연결의 경우에는 반드시 두 번째 방법만 사용해야 합니다(첫 번째 방법은 작동하지 않습니다). 왜냐하면 이 연결은 브라우저 내부적으로 제어되는 것이라 CSS를 통해 anchor-name을 임의로 제거할 수 없기 때문이죠.
예를 들어, 커스터마이징 가능한 <select> 요소의 피커가 <select> 자체에 앵커링되는 것을 막고 싶다면, 다음과 같은 규칙을 사용할 수 있습니다.
::picker(select) {
position-anchor: none;
}
여러 개의 앵커 요소에 동일한 anchor-name 값이 주어지고, 위치된 요소가 그 이름을 자신의 position-anchor 속성값으로 갖게 되면 어떻게 될까요? 이때 위치된 요소는 소스 코드의 순서상 가장 마지막에 있는 동일한 anchor-name을 가진 앵커 요소와 연결됩니다.
예를 들어, 문서에 동일한 컴포넌트가 반복해서 렌더링되어 있고 각 컴포넌트마다 앵커에 묶인 요소가 하나씩 있다고 가정해 봅시다. 만약 각 컴포넌트가 서로 다른 앵커 이름을 쓰지 않는다면, 모든 위치된 요소들이 페이지의 제일 마지막에 있는 컴포넌트의 앵커 하나에만 전부 다닥다닥 붙어버리게 될 겁니다. 이건 분명 여러분이 원하는 동작이 아니겠죠!
이 문제를 해결하기 위해 anchor-scope 속성을 사용할 수 있습니다. 이 속성은 특정 하위 트리(subtree, 부모-자식 DOM 구조) 안에서만 anchor-name 값의 가시성(visibility), 즉 "스코프(영역)"를 제한합니다. 그 결과, 위치된 요소는 자신이 속한 하위 트리 안에 스코프가 설정되어 있을 경우, 오직 그 동일한 하위 트리 내부에 있는 앵커 요소에만 묶일 수 있게 됩니다.
anchor-scope: all은 하위 트리 안에 설정된 모든 anchor-name 값들이, 동일한 하위 트리 안에 있는 위치된 요소들에게만 유효하도록 스코프를 제한합니다.anchor-scope: --my-anchor, --my-anchor2는 명시된 특정 anchor-name 값들에 대해서만, 동일한 하위 트리 내부에서끼리만 바인딩이 가능하도록 스코프를 제한합니다.anchor-scope: none은 기본값이며, 앵커 스코핑이 설정되지 않았음을 의미합니다.예시를 볼까요? 여러 개의 <section> 컨테이너 안에 각각 앵커와 앵커-위치된 <div> 요소들이 들어있다고 해봅시다.
<section class="scoped">
<div class="anchor">⚓︎</div>
<div class="positioned">Positioned 1</div>
</section>
<section class="scoped">
<div class="anchor">⚓︎</div>
<div class="positioned">Positioned 2</div>
</section>
<section class="scoped">
<div class="anchor">⚓︎</div>
<div class="positioned">Positioned 3</div>
</section>
우리는 각 anchor <div>에 --my-anchor라는 anchor-name을 주어 앵커 요소로 만듭니다. 그런 다음, 각 positioned <div>에 절대 위치(absolute)와 position-anchor: --my-anchor, 그리고 position-area: right 값을 주어 --my-anchor라는 이름을 가진 요소에 상대적으로 배치되게 합니다. 마지막으로, 각 <section> 컨테이너에 anchor-scope: --my-anchor를 사용하여 앵커의 스코프(유효 범위)를 설정합니다.
.anchor {
anchor-name: --my-anchor;
}
.positioned {
position: absolute;
position-anchor: --my-anchor;
position-area: right;
}
.scoped {
anchor-scope: --my-anchor;
}
이렇게 하면 다음과 같이 올바른 포지셔닝 동작 결과를 얻을 수 있습니다.
각각의 위치된 요소는 자신이 속한 같은 <section> 요소 내부의 앵커를 기준으로 배치됩니다. 왜냐하면 각 <section> 요소가 --my-anchor에 대한 anchor-scope를 가지고 있기 때문이죠. 이 스코프 컨테이너 안의 위치된 요소들은 같은 컨테이너 내부의 my-anchor라는 이름을 가진 앵커만을 기준으로 배치될 수 있습니다.
만약 컨테이너들에 anchor-scope: --my-anchor를 설정하지 않았다면, 3개의 위치된 요소들은 모조리 페이지의 맨 마지막(세 번째) 앵커 옆으로 몰려서 배치되었을 겁니다.

👨🏫 부연 설명:
여러분이 컴포넌트 단위로 프론트엔드를 개발하실 때(예: 리스트의 각 아이템마다 '더보기' 팝업 버튼이 있는 경우), 고유한 앵커 이름을 동적으로 생성해 줄 필요 없이 래퍼(Wrapper) 컴포넌트에 이anchor-scope를 걸어주면 아주 손쉽게 스코프 이슈를 해결할 수 있습니다!
앞서 보았듯이, 위치된 요소를 앵커에 단순히 '연결'만 하는 것은 그 자체로는 별 쓸모가 없습니다. 우리의 진짜 목표는 위치된 요소를 연결된 앵커 요소를 기준으로 '배치'하는 것이죠. 이를 수행하는 방법은 세 가지가 있습니다. 인셋(inset) 속성에 CSS anchor() 함수 값을 설정하거나, position-area를 지정하거나, anchor-center 배치 값을 사용해 중앙에 정렬하는 것입니다.
참고:
CSS 앵커 포지셔닝은 위치된 요소의 기본 위치가 뷰포트를 넘어가 잘리게(overflow) 될 경우를 대비해, 대안(폴백) 위치를 지정하는 메커니즘도 제공합니다. 자세한 내용은 오버플로우를 위한 폴백 옵션 및 조건부 숨김 가이드를 확인하세요.
참고:
이 연결과 위치 지정이 정상적으로 작동하려면 앵커 요소는 화면에 렌더링되는 시각적인 DOM 노드여야만 합니다. 만약 앵커가 숨겨져 있다면(예:display: none), 위치된 요소는 앵커 대신 자신의 가장 가까운 조상 중 위치가 지정된(positioned ancestor) 요소를 기준으로 배치됩니다. 앵커가 사라졌을 때 앵커에 위치된 요소도 함께 숨기는 방법은 조건부 숨김 가이드에서 다루고 있습니다.
anchor() 함수 값 사용하기 (Using inset properties with anchor() function values)전통적으로 absolute나 fixed 위치를 갖는 요소들은 인셋 속성(inset properties)(예: top, left, right, bottom)에 <length>나 <percentage> 값을 명시적으로 주어 위치를 잡았습니다. position: absolute의 경우 이 인셋 값은 가장 가까운 포지셔닝된 조상의 모서리로부터의 절대적인 거리이고, position: fixed의 경우는 뷰포트(브라우저 창) 모서리로부터의 절대적인 거리였죠.
CSS 앵커 포지셔닝은 이 패러다임을 바꿔, 앵커에 위치된 요소들이 연결된 앵커 요소의 모서리를 기준으로 배치될 수 있게 해줍니다. 이 모듈은 anchor()라는 함수를 정의하는데요, 이 함수는 각각의 인셋 속성에 유효한 값으로 쓰일 수 있습니다. 이 함수를 사용하면 앵커 요소가 무엇인지, 앵커 요소의 어느 쪽 면(side)을 기준으로 삼을 것인지, 그리고 그 면으로부터 얼마나 떨어져 있을지를 정의하여 결과적으로 앵커 기준의 절대 거리를 인셋 값으로 산출하게 됩니다.
이 함수의 구성은 다음과 같습니다.
anchor(<anchor-name> <anchor-side>, <fallback>)
<anchor-name>
배치의 기준으로 삼고 싶은 앵커 요소의 anchor-name 속성값입니다. 이 값은 <dashed-ident> 형식이어야 합니다. 이 항목을 생략하면, 해당 요소의 기본 앵커(default anchor)가 사용됩니다. (기본 앵커란 자신의 position-anchor 속성에 참조되어 있거나, 비표준 HTML 속성인 anchor를 통해 연결된 앵커를 말합니다.)
참고:
<anchor-name>을 직접 명시하면 그 특정 앵커를 기준으로 요소를 배치하게 되지만, 그렇다고 해서 두 요소가 근본적으로 '연결'되는 것은 아닙니다. 하나의 위치된 요소 안에서 여러 개의 인셋 속성에 각각 서로 다른<anchor-name>을 넣은anchor()함수를 사용해 여러 앵커를 기준으로 배치할 수도 있지만, 본질적으로 위치된 요소는 오직 '단 하나의 앵커'와만 결합(associated)됩니다.
<anchor-side>
앵커의 특정 면(side)을 기준으로 하는 위치를 지정합니다. 사용할 수 있는 유효한 값으로는 앵커의 center, 물리적 방향(top, left 등), 논리적 방향(start, self-end 등), 혹은 인셋 속성의 축(axis)에서 시작(0%)과 끝(100%) 사이를 의미하는 <percentage> 값이 있습니다. 만약 anchor() 함수가 적용되는 인셋 속성과 호환되지 않는(incompatible) 방향 값이 입력되면, 브라우저는 폴백(fallback) 값을 사용합니다.
<fallback>
해당 요소가 absolute나 fixed 위치를 갖지 않거나, 사용된 <anchor-side> 값이 현재 인셋 속성과 호환되지 않거나, 참조하려는 앵커 요소가 존재하지 않을 때 **대체용**으로 사용할 거리를 정의하는 <length-percentage> 값입니다.
anchor() 함수의 반환값은 앵커의 위치를 기반으로 계산된 길이(length) 값입니다. 만약 앵커-위치된 요소의 인셋 속성에 anchor() 함수 대신 고정된 길이나 퍼센트 값을 직접 입력해 버리면, 이 요소는 더 이상 앵커에 묶이지 않은 일반 요소처럼 배치됩니다. 이는 anchor-side 값이 인셋 속성과 맞지 않아 폴백(fallback) 값이 쓰일 때와 동일한 동작입니다.
아래 두 선언은 완전히 똑같은 결과를 낳습니다 (잘못된 사용의 예시).
bottom: anchor(right, 50px);
bottom: 50px;
bottom이라는 상하 방향(block 축) 인셋 속성에 right라는 좌우 방향(inline 축)을 기준으로 삼으려 했으니 호환이 안 됩니다. 결국 두 경우 모두 위치된 요소를 가장 가까운 포지셔닝된 조상(또는 초기 컨테이닝 블록)의 바닥으로부터 50px 위쪽에 배치해 버릴 겁니다.
여러분이 실무에서 가장 자주 사용하게 될 anchor() 파라미터는 그냥 기본 앵커의 특정 면(side)을 가리키는 방식일 겁니다. 앵커와 위치된 요소 사이에 간격을 띄우고 싶을 텐데, 이때는 보통 margin을 추가하거나, 아니면 calc() 함수 안에서 anchor()를 감싸서 바로 거리를 더해주는 방식을 씁니다.
예를 들어, 아래의 CSS 규칙은 위치된 요소의 왼쪽(left) 모서리를 앵커 요소의 오른쪽(right) 모서리에 딱 맞붙게 한 다음, margin-left를 줘서 사이 간격을 살짝 벌려줍니다.
.positionedElement {
left: anchor(right);
margin-left: 10px;
}
anchor() 함수의 결괏값은 '길이(length)' 데이터이기 때문에, calc() 함수 내부에서 자유롭게 연산할 수 있습니다. 마진(margin)을 따로 쓰는 대신 calc()를 활용해 앵커의 시작 모서리로부터 10px 떨어진 위치를 한 줄로 선언해 볼게요.
.positionedElement {
inset-block-end: calc(anchor(start) + 10px);
}
anchor() 예제실제로 anchor()가 어떻게 동작하는지 볼까요? 이전 예제와 동일한 HTML을 사용하되, 스크롤을 만들어내기 위해 위아래로 넘치는 더미 텍스트를 추가했습니다. 앵커 요소의 anchor-name 역시 이전과 동일하게 유지해 보죠.
.anchor {
anchor-name: --my-anchor;
}
이제 정보 박스(infobox)는 이 앵커 이름을 통해 앵커와 연결되고 고정된(fixed) 위치를 갖습니다. 우리는 이 요소에 inset-block-start 속성(가로 쓰기 모드에서의 top과 동일)과 inset-inline-start 속성(가로 쓰기 모드에서의 left와 동일)을 주입해서 완벽하게 묶어(tether) 버릴 겁니다. 간격을 띄우기 위해 margin도 살짝 줄게요.
.infobox {
position-anchor: --my-anchor;
position: fixed;
inset-block-start: anchor(end);
inset-inline-start: anchor(self-end);
margin: 5px 0 0 5px;
}
이 인셋 속성의 배치 선언들을 조금 더 자세히 파헤쳐 봅시다.
inset-block-start: anchor(end): 위치된 요소의 블록 시작(block start, 윗면) 모서리를 앵커의 블록 끝(block end, 아랫면) 모서리에 맞춥니다.inset-inline-start: anchor(self-end): 위치된 요소의 인라인 시작(inline start, 좌측면) 모서리를 앵커의 인라인 끝(inline end, 우측면) 모서리에 맞춥니다.이제 문서를 위아래로 스크롤 해보시면, 위치된 요소가 앵커 요소와의 상대적 위치를 끈질기게 유지하는 걸 보실 수 있습니다. 즉, 브라우저 뷰포트가 아니라 '앵커'에 단단히 고정(fixed)된 것이죠!


position-area 설정하기 (Setting a position-area)position-area 속성은 앵커를 기준으로 요소를 배치할 때 anchor() 함수를 대신할 수 있는 아주 훌륭한 대안입니다. position-area 속성은 정중앙에 앵커 요소가 놓여있는 3x3 그리드(타일) 개념을 기반으로 작동합니다. 이 속성을 사용하면 9개의 타일 중 원하는 곳 어디든 위치된 요소를 쏙 넣을 수도 있고, 2개나 3개의 타일에 걸쳐서 쭉 늘릴(span) 수도 있습니다.
그리드 타일은 행(row)과 열(column)로 나뉘어 있습니다.
top, center, bottom으로 표현됩니다. 또한 start, center, end 같은 논리적 값이나 y-start, center, y-end 같은 좌표계 값으로도 표현할 수 있습니다.left, center, right로 표현됩니다. 역시 start, center, end 같은 논리적 값이나 x-start, center, x-end 같은 좌표계 값으로 쓸 수 있죠.가운데 타일(Center tile)의 크기는 앵커 요소의 컨테이닝 블록(containing block)에 의해 결정되며, 가운데 타일과 그리드의 바깥쪽 가장자리 사이의 거리는 위치된 요소의 컨테이닝 블록에 의해 정의됩니다.
position-area 속성값은 위에서 설명한 행과 열 값들을 조합해 1개 혹은 2개의 값으로 구성되며, 요소가 어느 영역에 걸쳐서(span) 배치될지 정의할 수 있는 옵션도 제공합니다.
예를 들어 볼까요?
특정 그리드 1칸에 요소를 넣기 위해 두 개의 값을 명시할 수 있습니다.
top left (논리적 표현으로는 start start): 좌측 상단 사각형에 위치된 요소를 배치합니다.bottom center (논리적 표현로는 end center): 하단 중앙 사각형에 배치합니다.행 또는 열 값에 span-* 값을 더해서 쓸 수도 있습니다. 첫 번째 값은 배치할 행/열의 기준을 정하고(처음엔 그 중심에 배치됨), 두 번째 값은 어느 방향으로 얼만큼 칸을 차지할지(span)를 지정합니다.
top span-left: 윗줄(top row)에 요소를 배치하되, 그 줄의 '가운데 타일'부터 '왼쪽 타일'까지 두 칸에 걸쳐 넓게 배치합니다.y-end span-x-end: Y축 끝(맨 아랫줄)에 배치하고, 그 열의 '가운데'부터 'x축 끝(오른쪽)' 방향으로 걸쳐서 배치합니다.block-end span-all: 블록 끝 줄(맨 아랫줄)에 배치하고, 해당 줄의 인라인 시작, 중앙, 인라인 끝까지 3개의 타일 전체(all)를 쭉 덮어버리도록 배치합니다.만약 단 1개의 값만 지정한다면, 그 값이 무엇인지에 따라 브라우저가 알아서 작동 방식을 결정합니다.
top, bottom, left, right)이나 좌표계 값(y-start, y-end, x-start, x-end)을 하나만 주면, 나머지 한쪽 방향은 span-all로 취급됩니다. 예를 들어 top만 쓰면 top span-all을 쓴 것과 똑같은 결과가 나옵니다.start나 end)을 하나만 주면, 가로와 세로 두 값이 모두 똑같은 값으로 복제된 것처럼 취급됩니다. 즉, start 하나만 쓰면 start start와 같습니다.center 하나만 쓰면 양쪽 모두 center가 적용됩니다 (즉, center center).참고:
쓸 수 있는 모든 값에 대한 상세 설명은<position-area>값 참고 페이지를 꼭 확인해 보세요. 물리적 값(top등)과 논리적 값(start등)을 섞어서 쓰면 선언이 무효화(invalidate) 되니 주의하셔야 합니다!
💡 강사 팁:
position-area는 복잡한anchor()함수와calc()사칙연산 없이, 머릿속에 3x3 틱택토 보드판을 그리고 원하는 구역을 직관적으로 툭 던져줄 수 있는 마법 같은 기능입니다. UI 상에서 위치를 빠르게 전환해야 하는 툴팁 개발 시에 생산성을 엄청나게 높여줍니다!
직접 값을 선택하면서 테스트해 볼 수 있도록 드롭다운 메뉴(<select>)를 연결한 코드를 작성해 보았습니다.
<form>
<label for="position-area-select">Choose a position-area:</label>
<select id="position-area-select" name="position-area-select">
<option>top</option>
<option>bottom</option>
</select>
</form>
/* CSS 스타일링 */
.infobox {
position: fixed;
position-anchor: --my-anchor;
position-area: top;
}
/* JavaScript로 셀렉트 박스 제어하기 */
const infobox = document.querySelector(".infobox");
const selectElem = document.querySelector("select");
selectElem.addEventListener("change", () => {
const area = selectElem.value;
// 셀렉트 박스에서 선택된 값을 position-area에 주입합니다.
infobox.style.positionArea = area;
});
정보 박스는 초기에 position-area: top; 속성을 받아 3x3 그리드의 맨 윗줄 전체를 차지하는 위치에 렌더링 됩니다. 드롭다운 메뉴에서 다른 값을 선택하면 즉시 위치가 변경되면서 그리드의 위력을 체감하실 수 있을 겁니다.



위의 예제에서 우리는 위치된 요소에 명시적으로 너비나 높이 사이즈를 지정하지 않았습니다. 일부러 생략한 건데요, 이렇게 했을 때 어떤 동작이 일어나는지 보여드리기 위해서입니다.
위치된 요소가 크기 지정 없이 position-area 그리드 셀에 배치되면, 지정된 그리드 영역에 맞게 정렬되고 너비(width)가 max-content로 설정된 것처럼 동작합니다. 즉, 자기가 품고 있는 콘텐츠(텍스트 등)의 너비만큼 컨테이닝 블록 크기가 결정되는 것이죠. 이 크기는 position: fixed 속성이 부여되면서 강제된 결과입니다.
자동으로 사이즈가 정해지는 이 absolute나 fixed 요소들은 텍스트 내용물을 담기 위해 필요한 만큼 옆으로 쫙 늘어나지만, 뷰포트의 가장자리에 닿으면 더 이상 늘어나지 못하고 제약을 받습니다.
예를 들어, left나 inline-start 속성을 받아 그리드의 왼쪽 타일에 배치되었는데 글이 길면, 텍스트가 밑으로 줄바꿈(wrap) 됩니다. 만약 위치된 요소가 가진 콘텐츠의 max-content 고유 너비가 앵커보다 좁거나 짧다면, 빈 공간을 메우기 위해 앵커 크기만큼 억지로 늘어나지는 않습니다.
반면 위치된 요소가 position-area: bottom center처럼 수직선상 가운데에 정렬된다면 어떻게 될까요? 요소는 지정된 그리드 셀에 맞춰 정렬되고, 그 너비는 앵커 요소의 너비와 똑같이 맞춰집니다. 이 경우 요소의 최소 높이(min-height)는 앵커 요소의 컨테이닝 블록 크기가 됩니다. 이 요소는 뷰포트를 벗어나 넘치지(overflow) 않는데, 이는 브라우저가 요소의 min-width를 min-content로 잡기 때문입니다. 즉, 문장 속에서 가장 긴 단어의 길이보다는 최소한 넓게 폭을 확보한다는 뜻입니다.
anchor-center를 사용하여 앵커의 중앙에 맞추기 (Centering on the anchor using anchor-center)position-area 속성의 center 값들을 이용해 앵커에 위치된 요소를 중앙 정렬할 수도 있지만, 직접 인셋(inset) 속성과 anchor() 함수를 결합해 사용하면 훨씬 더 정밀한 위치 제어가 가능합니다. CSS 앵커 포지셔닝은 position-area가 아닌 인셋 속성을 활용해 요소를 묶어줄 때, 앵커를 기준으로 요소를 아주 쉽게 중앙에 맞출 수 있는 새로운 값 하나를 제공합니다.
우리가 자주 쓰는 정렬 속성인 justify-self, align-self, justify-items, align-items (그리고 이들의 축약형인 place-items와 place-self)는 다양한 레이아웃 시스템에서 요소를 인라인(가로) 또는 블록(세로) 방향으로 쉽게 정렬하게 해줍니다. (Flexbox의 주축/교차축 정렬을 떠올리시면 됩니다.) CSS 앵커 포지셔닝은 이 속성들에 넣을 수 있는 새로운 값인 anchor-center를 추가로 제공합니다. 이 값을 주면 위치된 요소가 기본 앵커의 정중앙에 완벽히 정렬됩니다!
아래 예제를 보시죠. 정보 박스는 앵커의 밑면(bottom edge)에 묶여 있습니다. 그런 다음 justify-self: anchor-center를 사용하여 앵커의 정중앙을 기준으로 정보 박스가 가로로 딱 가운데 정렬되도록 만들었습니다.
.infobox {
position: fixed;
position-anchor: --my-anchor;
top: calc(anchor(bottom) + 5px);
justify-self: anchor-center;
}
이렇게 하면 앵커 요소의 아래쪽 중앙에 완벽하게 정보 박스를 띄울 수 있습니다. 마치 과녁의 정중앙을 맞춘 것처럼요!
요소를 앵커의 '위치'에 상대적으로 배치할 수 있을 뿐만 아니라, 앵커의 '크기(Size)'에 맞춰 요소의 크기를 조정할 수도 있습니다. CSS 크기 지정 속성 값에 anchor-size() 함수를 사용하면 됩니다.
anchor-size() 값을 받아들일 수 있는 크기 관련 CSS 속성들은 다음과 같습니다:
widthheightmin-widthmin-heightmax-widthmax-heightblock-sizeinline-sizemin-block-sizemin-inline-sizemax-block-sizemax-inline-sizeanchor-size() 함수가 실행되면 최종적으로 계산된 <length> 값을 반환합니다. 작성하는 문법 구조는 아래와 같습니다.
anchor-size(<anchor-name> <anchor-size>, <length-percentage>)
<anchor-name>
크기 지정의 기준으로 삼고자 하는 앵커 요소의 anchor-name 속성값(<dashed-ident>)입니다. 만약 생략하면 해당 요소의 기본 앵커(position-anchor에 참조된 요소)가 대신 사용됩니다.
<anchor-size>
기준이 될 앵커 요소의 특정 치수(dimension)를 지정합니다. 물리적 차원인 width나 height를 쓸 수도 있고, 논리적 방향인 inline, block, self-inline, self-block을 사용할 수도 있습니다.
<length-percentage>
해당 요소가 절대 위치나 고정 위치가 아니거나 기준이 되는 앵커 요소가 존재하지 않을 때, 대타(fallback)로 사용할 크기 값을 지정합니다.
여러분이 이 기능을 사용할 때, 대부분은 단순히 기본 앵커의 치수 중 하나(예: width)를 불러와 쓰는 형태일 겁니다. 하지만 반환값이 길이라는 점을 이용해 calc() 함수 안에 집어넣고 마음대로 값을 증폭시키거나 깎아서 적용할 수도 있습니다.
예를 들어, 위치된 요소의 너비를 기본 앵커 요소의 너비와 완전히 똑같이 맞추고 싶다면 아래처럼 한 줄만 적으면 됩니다.
.elem {
width: anchor-size(width);
}
조금 더 응용해서, 앵커 요소 너비의 정확히 '4배'만큼 커지게 하고 싶다면 calc()를 감싸주세요.
.elem {
inline-size: calc(anchor-size(self-inline) * 4);
}
💡 강사 팁:
디자인 시스템을 구현하다 보면 드롭다운 메뉴 박스의 가로길이가 항상 부모 버튼의 길이와 똑같아야 할 때가 있죠? 이전엔 자바스크립트로 창 크기가 조절될 때마다 버튼 크기를 읽어와(Resize Observer 등 활용) 드롭다운에 입혀주던 눈물겨운 똥꼬쇼가 필요했지만... 이젠width: anchor-size(width);하나면 완전히 종결됩니다! 정말 멋지지 않나요?
예제를 살펴봅시다. 앵커 요소에 포커스(focus) 이벤트를 주기 위해 tabindex="0" 속성을 추가했습니다.
<div class="anchor" tabindex="0">⚓︎</div>
<div class="infobox">
<p>Infobox.</p>
</div>
.anchor {
anchor-name: --my-anchor;
width: 100px;
height: 100px;
transition: 1s all;
}
.infobox {
position-anchor: --my-anchor;
position: absolute;
height: 100px;
width: 100px;
}
이제 진짜 재밌는 부분입니다. 마우스를 올리거나(:hover) 포커스가 갔을 때(:focus), 앵커의 너비(width)가 300px로 쭈욱 늘어나게 만들 겁니다. 그리고 정보 박스(infobox)에 다음과 같은 설정을 부여합니다.
top 값에 anchor(top)을 줍니다. 정보 박스의 윗면이 앵커의 윗면 라인에 나란히 유지되게 하려는 목적이죠.left 값에 anchor-size(width)를 줍니다. 이러면 가장 가까운 포지셔닝된 조상의 왼쪽 모서리부터 시작해서, '앵커의 너비값'만큼 떨어진 곳에 정보 박스의 왼쪽 면이 놓이게 됩니다. 여기선 조상이 <body> 요소이므로 앵커 바로 오른쪽 옆에 나란히 놓이게 되는 마법이 펼쳐집니다.margin-left 값에 calc(anchor-size(width) / 4)를 줍니다. 앵커 너비의 정확히 4분의 1만큼만 앵커와 정보 박스 사이를 벌려(margin) 주게 됩니다. 앵커 크기가 변하면 마진 여백도 실시간으로 비례해서 쓱쓱 변한답니다!.anchor:hover,
.anchor:focus {
width: 300px;
}
.infobox {
top: anchor(top);
left: anchor-size(width);
margin-left: calc(anchor-size(width) / 4);
}
실행 결과를 보시고 앵커에 마우스를 올려보세요. 앵커가 커지는 1초 동안, 정보 박스가 밀려나는 위치와 마진 여백도 앵커 크기에 완벽히 비례해서 부드럽게 늘어나는 것을 확인하실 수 있습니다!
anchor-size()의 다른 쓰임새 (Other uses of anchor-size())anchor-size()는 방금 보신 것처럼 크기를 정의할 때뿐만 아니라, 물리적 및 논리적 인셋(inset) 속성이나 마진(margin) 속성에서도 훌륭하게 활용될 수 있습니다.
anchor-size() 함수를 인셋 속성(inset property) 값 안에 사용하면 앵커 크기를 기준으로 위치를 조절할 수 있습니다.
left: anchor-size(width);
inset-inline-end: anchor-size(--my-anchor height, 100px);
이 방식은 anchor() 함수나 position-area 속성을 썼을 때처럼 앵커의 '위치' 좌표 자체를 추적해서 묶어두는 방식은 아닙니다. (앞서 살펴본 앵커를 기준으로 요소 위치 지정하기와는 다릅니다.) 즉, 앵커의 위치가 이동한다고 해서 요소가 그 위치를 졸졸 따라가지는 않아요. 일반적인 absolute나 fixed 포지셔닝 규칙을 따르면서 숫자만 계산해 오는 식입니다.
하지만 이 방식이 유용할 때가 있습니다. 예를 들어, 앵커가 위아래 수직으로만 움직이면서, 가로축으로는 항상 부모 컨테이너의 맨 왼쪽 가장자리에 딱 붙어있어야 하는 특수한 상황을 떠올려 보세요. 이때 left: anchor-size(width)를 사용하면, 앵커 너비가 아무리 지멋대로 변하더라도 정보 박스는 늘 앵커의 우측(앵커 너비만큼 떨어진 곳)에 안전하게 자리 잡게 만들 수 있습니다.
또한 anchor-size() 함수를 margin-* 속성 값 내부에 사용해 앵커 크기에 비례하는 여백을 줄 수 있습니다.
margin-left: calc(anchor-size(width) / 4);
margin-block-start: anchor-size(--my-anchor self-block, 20px);
이 테크닉은 앵커 요소의 크기가 동적으로 변하더라도 항상 "앵커 너비의 n%" 비율을 유지하는 마진 간격을 연출하고 싶을 때 매우 유용합니다.
anchor-size() 위치 및 마진 활용 예시 (anchor-size() position and margin example)방금 배운 이 모든 내용이 종합적으로 적용된 실전 코드를 다시 한번 눈으로 익혀두시면 CSS 앵커 포지셔닝에 대한 두려움은 완전히 사라지실 겁니다.