안녕하세요! 프론트엔드 개발의 세계로 오신 것을 환영합니다. 오늘은 CSS의 최신 마법 중 하나인 CSS 앵커 포지셔닝(Anchor positioning)에 대해 조금 더 깊이 들어가 볼 거예요.
CSS 앵커 포지셔닝을 사용할 때 가장 중요하게 고려해야 할 점은 무엇일까요? 바로 앵커(기준점)가 화면 어디에 있든 상관없이, 앵커에 연결된 요소(예: 툴팁이나 팝업)가 항상 사용자가 상호작용하기 편한 위치에 표시되도록 보장하는 것입니다.
예를 들어, 페이지를 스크롤하다 보면 앵커와 그에 연결된 요소가 뷰포트(화면) 가장자리로 이동하게 되죠. 이때 연결된 요소가 화면 밖으로 삐져나가버리는(overflow) 상황이 발생하면 어떻게 해야 할까요? 요소의 위치를 변경해서 다시 화면 안으로 집어넣어야 합니다. 가장 쉬운 예로, 앵커의 반대편으로 위치를 휙! 뒤집어버리는 방법이 있겠죠.
아니면 상황에 따라 화면 밖으로 넘쳐흐르는 요소를 아예 숨겨버리는 게 더 나을 때도 있습니다. 만약 기준점이 되는 앵커 자체가 화면 밖으로 사라졌다면, 그에 연결된 툴팁만 덩그러니 남아있는 건 문맥상 이상할 테니까요.
이 가이드에서는 이러한 문제들을 우아하게 해결하기 위해 CSS 앵커 포지셔닝 메커니즘인 position-try 폴백 옵션(position-try fallback options)과 조건부 숨김(conditional hiding)을 사용하는 방법을 자세히 설명해 드릴게요!
position-try 폴백 옵션은 연결된 요소가 화면 밖으로 벗어나려고 할 때, 브라우저가 요소를 계속 화면 안에 유지시키기 위해 시도해 볼 수 있는 '대체 위치'들을 제공합니다. 참고: CSS 앵커 포지셔닝의 아주 기초적인 내용이 궁금하시다면 CSS 앵커 포지셔닝 사용하기(Using CSS anchor positioning) 문서를 먼저 읽어보시길 추천합니다!
💡 강사님의 꿀팁:
과거에는 스크롤 할 때 툴팁이 화면 밖으로 잘리지 않게 하려면 JavaScript로 화면 높이, 스크롤 위치, 요소의 너비/높이를 일일이 계산해야 했어요. (정말 피곤한 작업이었죠 😅) 하지만 이제 소개할 CSS 속성들을 활용하면, 브라우저가 알아서 똑똑하게 위치를 재조정해 준답니다. 프론트엔드 개발자들의 삶의 질을 팍팍 올려주는 엄청난 기능이니 꼭 마스터해 보세요!
만약 어떤 UI 요소의 오른쪽 상단에 툴팁을 고정시켜 놓았다고 가정해 볼게요. 사용자가 화면을 스크롤해서 그 UI 요소가 뷰포트의 오른쪽 최상단 구석으로 이동하게 되면, 당연히 그 요소에 붙어있던 툴팁은 화면 밖으로 밀려나 안 보이게 될 겁니다.
CSS 앵커 포지셔닝은 바로 이런 문제를 해결해 줍니다! 이 모듈의 position-try-fallbacks 속성을 사용하면, 브라우저가 요소의 오버플로우를 막기 위해 시도해 볼 수 있는 하나 이상의 '대체 위치 옵션(fallback options)'을 지정할 수 있습니다.
position-try 폴백 옵션은 다음 세 가지 방법으로 지정할 수 있어요:
position-area 값 사용.@position-try 앳룰(@-rule)을 사용하여 정의한 커스텀 옵션 (Custom options) 사용.여기에 더해, position-try-order 속성을 사용하면 요소의 초기 위치를 결정할 때 어떤 옵션을 우선적으로 적용할지 지정할 수 있습니다. 예를 들어, "화면의 상하좌우 중 빈 공간(높이나 너비)이 가장 많은 쪽에 요소를 처음부터 띄워줘!"라고 설정할 수 있는 거죠.
참고로, 단축 속성인 position-try를 사용하면 position-try-order와 position-try-fallbacks 값을 한 줄의 선언으로 깔끔하게 작성할 수도 있습니다.
한편, 앵커가 화면 밖으로 나갔는데 연결된 콘텐츠만 화면에 남아있는 게 어색한 상황도 있습니다 (물론 그 반대도 마찬가지고요). 퀴즈 문제(앵커)와 정답(연결된 요소)을 짝지어 보여주는 경우를 상상해 보세요. 둘 다 화면에 같이 보이거나, 아예 둘 다 안 보이게 하는 것이 훨씬 자연스럽겠죠? 이는 조건부 숨김(conditional hiding)을 통해 구현할 수 있으며, position-visibility 속성으로 관리합니다. 이 속성에는 오버플로우가 발생할 때 요소를 숨길지 말지 결정하는 다양한 조건 값들을 넣을 수 있습니다.
position-try-fallbacks 속성에는 미리 만들어져 있는 유용한 폴백 옵션 값들(명세서에서는 이를 <try-tactic>이라고 부릅니다)이 있습니다. 이 값들은 요소가 화면을 벗어나려고 할 때, 한 축 또는 두 축을 기준으로 요소의 위치를 거울처럼 "뒤집어(flip)" 줍니다.
요소는 블록 축을 기준으로 뒤집히거나 (flip-block), 인라인 축을 기준으로 뒤집히거나 (flip-inline), 혹은 앵커의 모서리에서 중심을 관통해 반대쪽 모서리로 이어지는 가상의 대각선을 기준으로 뒤집힐 수 있습니다 (flip-start).
처음 두 값(flip-block, flip-inline)은 요소를 정반대 편으로 넘겨서 위치를 대칭시키고, flip-start는 요소를 인접한 측면으로 뒤집습니다.
예를 들어, 앵커보다 10px 위에 띄워져 있던 요소가 뷰포트 위쪽으로 삐져나가려 한다면, flip-block 값을 주었을 때 브라우저가 요소를 앵커의 10px 아래쪽으로 휙 뒤집어 위치시킵니다.
💡 강사님의 꿀팁:
이해하기 쉽게 설명해 드릴게요!flip-block은 수직(위/아래)으로 휙 넘기는 것이고,flip-inline은 수평(좌/우)으로 넘기는 것이라고 외워두시면 편합니다. 툴팁이 위에 있다가 공간이 모자라면 아래로 도망가게 하려면flip-block을 쓰면 되는 거죠!
이 예제에서는 두 개의 <div> 요소를 사용해 보겠습니다. 첫 번째 요소는 우리의 앵커(기준점)가 될 것이고, 두 번째 요소는 그 앵커를 기준으로 배치될 요소입니다.
<div class="anchor">⚓︎</div>
<div class="infobox">
<p>This is an information box.</p>
</div>
우리는 앵커와 연결된 요소가 뷰포트 내에서 수평/수직으로 자유롭게 스크롤될 수 있도록 <body> 요소를 뷰포트보다 크게 스타일링할 것입니다.
body {
width: 1500px;
height: 500px;
}
시각적으로 잘 보이게 하기 위해, 앵커에 절대 위치(absolute positioning)를 주어 초기 화면의 대략 중앙쯤에 나타나도록 만들었습니다.
.anchor {
font-size: 1.8rem;
color: white;
text-shadow: 1px 1px 1px black;
background-color: hsl(240 100% 75%);
width: fit-content;
border-radius: 10px;
border: 1px solid black;
padding: 3px;
}
.anchor {
anchor-name: --my-anchor;
position: absolute;
top: 100px;
left: 45%;
}
이제 앵커에 연결된 요소(infobox)에 고정 위치(fixed)를 부여하고, position-area를 사용하여 앵커의 왼쪽 상단 모서리(top left)에 묶어(tether) 줍니다. 그리고 position-try-fallbacks: flip-block, flip-inline; 코드를 추가하여 앵커가 뷰포트 가장자리에 가까워질 때 요소가 오버플로우 되지 않도록 움직일 수 있는 몇 가지 폴백 옵션을 제공합니다.
.infobox {
color: darkblue;
background-color: azure;
border: 1px solid #dddddd;
padding: 10px;
border-radius: 10px;
font-size: 1rem;
}
.infobox {
position: fixed;
position-anchor: --my-anchor;
position-area: top left;
position-try-fallbacks: flip-block, flip-inline;
}
참고: 여러 개의
position-try폴백 옵션을 쉼표(,)로 구분해서 나열하면, 코드를 작성한 순서대로 브라우저가 위치 조정을 시도(try)합니다.
실제 코드가 적용된 페이지를 스크롤해서 앵커를 뷰포트 모서리 근처로 이동시켜 보세요:
flip-block 동작)flip-inline 동작)그런데 앵커를 뷰포트의 왼쪽 상단 구석(top-left)으로 쭉 밀어보시면 뭔가 문제가 생긴 것을 발견할 수 있습니다. 툴팁 요소가 블록 축과 인라인 축 모두에서 넘쳐흐르기 시작하면, 원래의 기본 위치(왼쪽 상단)로 되돌아가 버린 채 양쪽 방향 모두에서 오버플로우가 발생하게 됩니다. 이건 우리가 원했던 결과가 아니죠!
왜 이런 일이 발생할까요? 브라우저에게 flip-block 또는(or) flip-inline 옵션만 주었을 뿐, "동시에 두 축 모두를 뒤집어봐라"라는 옵션은 주지 않았기 때문입니다. 브라우저는 지정된 폴백 옵션들을 차례대로 시도하면서 툴팁 요소가 뷰포트 안에 완전히 쏙 들어가는 완벽한 위치를 찾습니다. 만약 그런 위치를 찾지 못하면, 폴백 옵션 적용을 포기하고 처음 우리가 정의했던 원래 위치(기본 위치)에 그대로 렌더링 해버립니다.
다음 섹션에서는 이 답답한 문제를 어떻게 해결할 수 있는지 바로 알아볼게요!
미리 정의된 시도 옵션(try fallback options)들이나 우리가 직접 만든 커스텀 시도 옵션의 이름들을 스페이스바(공백)로 띄워 적으면 하나의 통합된 폴백 옵션 값으로 결합할 수 있습니다. 쉼표로 구분된 position-try-fallbacks 리스트 안에 이렇게 묶은 값을 통째로 넣는 거죠. 브라우저가 이 결합된 폴백 옵션을 적용할 때는, 나열된 각각의 효과들을 동시에 결합하여 단일 옵션처럼 작동시킵니다.
자, 이 결합된 폴백 옵션을 사용해서 앞선 예제에서 발생했던 구석 넘침(overflow) 문제를 해결해 보겠습니다. HTML과 기본적인 CSS는 동일하지만, infobox의 포지셔닝 스타일만 살짝 바꿨습니다. 세 번째 옵션으로 flip-block flip-inline이라는 결합된 옵션을 추가해 주었어요!
<div class="anchor">⚓︎</div>
<div class="infobox">
<p>This is an information box.</p>
</div>
body {
width: 1500px;
height: 500px;
}
.anchor {
font-size: 1.8rem;
color: white;
text-shadow: 1px 1px 1px black;
background-color: hsl(240 100% 75%);
width: fit-content;
border-radius: 10px;
border: 1px solid black;
padding: 3px;
}
.anchor {
anchor-name: --my-anchor;
position: absolute;
top: 100px;
left: 45%;
}
.infobox {
color: darkblue;
background-color: azure;
border: 1px solid #dddddd;
padding: 10px;
border-radius: 10px;
font-size: 1rem;
}
.infobox {
position: fixed;
position-anchor: --my-anchor;
position-area: top left;
position-try-fallbacks:
flip-block,
flip-inline,
flip-block flip-inline;
}
이렇게 코드를 짜면, 브라우저는 가장 먼저 flip-block을 시도하고 그다음 flip-inline을 시도해서 오버플로우를 막으려 노력할 것입니다. 만약 이 두 옵션이 모두 실패한다면? 이제 브라우저는 이 둘을 결합한 세 번째 옵션인 flip-block flip-inline을 시도하여, 요소의 위치를 블록 방향과 인라인 방향 동시에 모두 뒤집어 버립니다.
이제 앵커를 뷰포트의 위쪽 모서리와 왼쪽 모서리가 만나는 가장자리(왼쪽 상단 구석)로 스크롤해보면, 드디어 요소가 앵커의 오른쪽 아래(bottom-right)로 예쁘게 휙 뒤집히는 것을 확인할 수 있습니다!
💡 강사님의 꿀팁:
이 방법은 모바일 환경처럼 뷰포트 크기가 작고 제약이 많은 화면에서 특히 빛을 발합니다. 모서리에 닿았을 때 대각선 반대편으로 영리하게 도망가는 툴팁을 보면 정말 속이 다 시원해지거든요.
position-area 폴백 옵션 사용하기 (Using position-area try fallback options)앞서 배운 미리 정의된 <try-tactic> 폴백 옵션들도 충분히 유용하긴 하지만, 축을 기준으로 위치를 '대칭으로 뒤집는(flip)' 것만 가능하다는 한계가 있습니다. 만약 앵커의 왼쪽 상단(top-left)에 있던 요소를 화면이 좁아질 때 단순히 뒤집는 게 아니라, 앵커의 바로 아래쪽(bottom) 중앙으로 옮기고 싶다면 어떻게 해야 할까요?
이럴 때는 position-area 속성 값을 그대로 position-try-fallbacks 리스트 안에 폴백 옵션으로 사용할 수 있습니다. 이렇게 하면 브라우저는 해당 position-area를 기반으로 자동으로 새로운 시도(try) 옵션을 만들어냅니다. 사실상 position-area 속성 값 하나만 달랑 포함된 커스텀 포지션 옵션을 빠르게 생성하는 지름길(shortcut)이라고 볼 수 있죠.
다음 예제에서는 position-area 값을 활용한 폴백 옵션들을 보여줍니다. HTML과 기본 CSS는 똑같지만, infobox의 포지셔닝 방식을 바꿨습니다. 이번에는 폴백 옵션으로 position-area 값인 top, top-right, right, bottom-right, bottom, bottom-left, left를 모조리 넣어봤습니다. 이렇게 하면 앵커가 뷰포트의 어떤 가장자리로 이동하더라도, 툴팁 요소가 항상 그럴싸하고 합리적인 위치에 표시될 수 있습니다.
단순히 거울처럼 뒤집는 방식(predefined values)보다 이렇게 하나하나 나열하는 방식이 코드가 조금 더 길어지긴(verbose) 하지만, 우리가 원하는 위치를 훨씬 더 세밀하고 유연하게(granular and flexible) 컨트롤할 수 있답니다.
<div class="anchor">⚓︎</div>
<div class="infobox">
<p>This is an information box.</p>
</div>
body {
width: 1500px;
height: 500px;
}
.anchor {
font-size: 1.8rem;
color: white;
text-shadow: 1px 1px 1px black;
background-color: hsl(240 100% 75%);
width: fit-content;
border-radius: 10px;
border: 1px solid black;
padding: 3px;
}
.anchor {
anchor-name: --my-anchor;
position: absolute;
top: 100px;
left: 45%;
}
.infobox {
color: darkblue;
background-color: azure;
border: 1px solid #dddddd;
padding: 10px;
border-radius: 10px;
font-size: 1rem;
}
.infobox {
position: fixed;
position-anchor: --my-anchor;
position-area: top left;
position-try-fallbacks:
top, top right, right,
bottom right, bottom,
bottom left, left;
}
참고: 아쉽지만
position-area값은 스페이스바(공백)로 구분하는 여러 개 결합 방식(combined position option) 안에는 섞어서 쓸 수 없습니다. 쉼표로 구분된 목록의 독립된 값으로만 사용해야 합니다.
페이지를 스크롤해서 앵커가 뷰포트 모서리 근처로 갈 때, 이 촘촘하게 짜여진 position-area 폴백 옵션들이 어떤 마법 같은 효과를 보여주는지 직접 확인해 보세요!
위에서 배운 방법들만으로는 내가 원하는 완벽한 위치나 스타일을 잡아내지 못하셨나요? 그렇다면 @position-try 앳룰(@-rule)을 사용하여 처음부터 끝까지 내 맘대로 작동하는 '커스텀 위치 폴백 옵션'을 직접 만들 수 있습니다. 작성하는 문법은 아래와 같아요:
@position-try --try-fallback-name {
descriptor-list
}
여기서 --try-fallback-name은 개발자인 우리가 마음대로 짓는 커스텀 폴백 옵션의 이름입니다. 이름을 짓고 나면, position-try-fallbacks 속성의 쉼표로 구분된 리스트 안에 이 이름을 당당하게 적어주면 됩니다.
만약 같은 이름을 가진 @position-try 규칙이 여러 개 있다면, CSS의 기본 원칙에 따라 문서 순서상 가장 마지막에 작성된 규칙이 이전 규칙들을 덮어씁니다. 또한, 폴백 옵션의 이름과 앵커 이름(또는 다른 CSS 변수 이름)을 똑같이 짓는 것은 피해주세요. 문법적으로 오류가 나서 깨지지는 않지만, 나중에 CSS 코드를 읽고 유지보수할 때 엄청난 혼란을 야기할 수 있습니다.
중괄호 {} 안의 descriptor-list에는 이 특정 커스텀 옵션이 적용되었을 때 요소가 어떻게 배치되고(placed), 크기가 어때야 하며(sized), 여백(margins)은 어떻게 설정할지에 대한 속성 값들을 정의합니다. 다만 아무 속성이나 다 쓸 수 있는 건 아니고, 오직 아래 나열된 제한적인 속성(descriptors)들만 허용됩니다:
position-areatop, left, inset-block 등)margin-left, margin-block-start 등)align-self, justify-self)width, block-size 등)position-anchor우리가 앳룰(@position-try) 안에 정성껏 작성해 둔 이 속성 값들은, 브라우저가 위기 상황에 처해 이 커스텀 폴백 옵션을 선택하게 되는 순간 요소에 즉시 덮어씌워집니다. 요소에 원래 적용되어 있던 기본 속성 값이 있었더라도, 폴백 옵션 안에 명시된 값으로 강제 교체(override)되는 거죠. 사용자가 다시 화면을 스크롤해서 여유 공간이 생기면(그래서 폴백이 필요 없어지거나 다른 폴백으로 교체되면), 이전에 적용되었던 커스텀 폴백의 값들은 흔적도 없이 싹 사라지고 원래 상태로 복구됩니다.
이번 예제에서는 커스텀 폴백 옵션을 여러 개 직접 세팅하고 활용해 보겠습니다. 역시나 HTML과 기본 뼈대 CSS는 동일합니다.
우선 @position-try를 이용해 네 개의 멋진 커스텀 폴백 옵션을 정의하는 것으로 시작해 볼까요:
<div class="anchor">⚓︎</div>
<div class="infobox">
<p>This is an information box.</p>
</div>
body {
width: 1500px;
height: 500px;
}
.anchor {
font-size: 1.8rem;
color: white;
text-shadow: 1px 1px 1px black;
background-color: hsl(240 100% 75%);
width: fit-content;
border-radius: 10px;
border: 1px solid black;
padding: 3px;
}
.anchor {
anchor-name: --my-anchor;
position: absolute;
top: 100px;
left: 45%;
}
.infobox {
color: darkblue;
background-color: azure;
border: 1px solid #dddddd;
padding: 10px;
border-radius: 10px;
font-size: 1rem;
}
@position-try --custom-left {
position-area: left;
width: 100px;
margin-right: 10px;
}
@position-try --custom-bottom {
position-area: bottom;
margin-top: 10px;
}
@position-try --custom-right {
position-area: right;
width: 100px;
margin-left: 10px;
}
@position-try --custom-bottom-right {
position-area: bottom right;
margin: 10px 0 0 10px;
}
이렇게 정성껏 커스텀 폴백 옵션들을 다 만들었으니, 이제 position-try-fallbacks 리스트에 그 이름들을 넣어 사용할 일만 남았습니다:
.infobox {
position: fixed;
position-anchor: --my-anchor;
position-area: top;
width: 200px;
margin-bottom: 10px;
position-try-fallbacks:
--custom-left, --custom-bottom, --custom-right, --custom-bottom-right;
}
툴팁(infobox)의 평화로운 기본 위치는 position-area: top으로 지정해 두었습니다. 즉, 툴팁이 화면 밖으로 넘치지 않는 평상시에는 앵커의 위쪽에 얌전하게 위치하며, position-try-fallbacks 속성은 조용히 잠을 자고 무시됩니다.
그리고 한 가지 더 눈여겨볼 점은 툴팁에 고정된 width: 200px과 margin-bottom: 10px이 설정되어 있다는 거예요. 이제 브라우저가 위기 상황을 맞아 각각의 커스텀 폴백 옵션을 적용할 때마다 이 고정값들이 어떻게 역동적으로 변하는지 관찰해 보겠습니다.
만약 툴팁이 오버플로우를 시작하면 브라우저는 긴급하게 구조 요청을 받고 position-try-fallbacks에 적힌 옵션들을 순서대로 꺼내 들기 시작합니다:
--custom-left를 시도합니다. 이 옵션은 툴팁을 앵커 왼쪽으로 옮기고 오른쪽 여백을 수정하며, 심지어 너비(width)를 100px로 줄여버립니다.--custom-bottom을 시도합니다. 툴팁을 앵커 아래로 옮기고 위쪽 여백을 줍니다. 여기에는 width를 바꾸는 코드가 없기 때문에, 툴팁의 너비는 원래 설정했던 기본 너비인 200px로 원래대로 돌아갑니다.--custom-right를 시도합니다. --custom-left와 매우 비슷하게 너비를 100px로 확 줄여버리고, 대신 오른쪽 위치에 맞게 여백(margin-left)을 반대로 설정해 줍니다.--custom-bottom-right를 시도합니다. 이건 툴팁을 앵커의 우측 하단 대각선 위치로 옮기면서 여백을 한 번에 제어합니다.참고로 이 모든 눈물겨운 폴백 시도들이 다 실패한다면, 툴팁은 운명을 받아들이고 다시 처음 지정했던 기본 위치인 position-area: top;으로 돌아갑니다.
참고 (중요):
시도(try) 폴백 옵션이 적용되면, 그 옵션 안에 정의된 값들이 원래 요소에 있던 기본값을 "강제로 덮어쓴다(override)"는 사실을 꼭 기억하세요! 앞서 설명했듯, 요소의 기본 너비는200px이지만--custom-right옵션이 적용되는 순간 너비는 무자비하게100px로 변신하게 됩니다.
💡 강사님의 꿀팁:
이 커스텀 방식은 디자이너가 "이 툴팁은 오른쪽으로 갈 땐 글씨가 좀 더 좁게, 아래로 갈 땐 널찍하게 보여주세요" 같은 복잡한 요구사항을 가져왔을 때 개발자를 살려주는 동아줄 같은 기능입니다.@position-try안에width나margin을 다르게 세팅해 두면 미디어 쿼리(Media Query) 부럽지 않은 반응형 컴포넌트를 만들 수 있어요!
position-try-order 사용하기 (Using position-try-order)position-try-order 속성은 지금까지 살펴본 다른 기능들과는 조금 다른 시각을 가지고 있습니다. 이 속성은 요소가 스크롤되다가 "화면 밖으로 넘어갈 뻔할 때(오버플로우 과정에서)" 폴백 옵션을 쓰는 것이 아니라, 애초에 툴팁 요소가 맨 처음 화면에 나타날 때(initial display) 어떤 폴백 옵션을 선택할지 결정하는 데 쓰입니다.
쉽게 말해, 이 속성을 쓰면 브라우저에게 "내가 설정해 둔 여러 위치 옵션 중에서, 요소가 들어갈 수 있는 공간(너비나 높이)이 가장 넉넉한 곳을 골라서 처음부터 거기에 띄워줘!"라고 명령할 수 있는 거죠. most-height, most-width, most-block-size, most-inline-size 같은 값들을 넣어주면 이 영리한 기능이 동작합니다. 물론, 기존에 설정했던 이 정렬 선호도를 끄고 싶다면 normal 값을 사용해 원상 복구할 수 있습니다.
만약 우리가 정의해둔 폴백 옵션들 중에 처음에 지정한 기본 위치보다 공간이 더 넉넉한 옵션이 아예 없다면, position-try-order는 그저 조용히 아무 일도 하지 않고 기본 위치를 유지합니다.
이 속성이 실제로 어떻게 동작하는지 재미있는 데모를 통해 확인해 볼까요? HTML 구조는 이전 예제들과 거의 같지만, 라디오 버튼(radio buttons)이 들어있는 <form>을 하나 추가했어요. 여기서 라디오 버튼을 클릭하면 position-try-order의 값을 실시간으로 바꿔보며 그 효과를 직접 눈으로 확인할 수 있습니다.
<div class="anchor">⚓︎</div>
<div class="infobox">
<p>This is an information box.</p>
</div>
<form>
<fieldset>
<legend>Choose a try order</legend>
<div>
<label for="radio-normal">normal</label>
<input
type="radio"
id="radio-normal"
name="position-try-order"
value="normal"
checked />
</div>
<div>
<label for="radio-most-height">most-height</label>
<input
type="radio"
id="radio-most-height"
name="position-try-order"
value="most-height" />
</div>
</fieldset>
</form>
요소를 앵커 밑에 달아주고 위쪽에 여백을 살짝 추가하는 커스텀 옵션 --custom-bottom을 미리 하나 만들어 두었습니다.
.anchor {
font-size: 1.8rem;
color: white;
text-shadow: 1px 1px 1px black;
background-color: hsl(240 100% 75%);
width: fit-content;
border-radius: 10px;
border: 1px solid black;
padding: 3px;
}
.anchor {
anchor-name: --my-anchor;
position: absolute;
top: 100px;
left: 45%;
}
.infobox {
color: darkblue;
background-color: azure;
border: 1px solid #dddddd;
padding: 10px;
border-radius: 10px;
font-size: 1rem;
text-align: center;
}
form {
position: fixed;
bottom: 2px;
right: 2px;
}
@position-try --custom-bottom {
top: anchor(bottom);
bottom: unset;
margin-top: 10px;
}
처음에는 infobox를 앵커의 위쪽(상단)에 나타나도록 설정하고, 우리가 방금 만든 커스텀 폴백 옵션을 지정해 줍니다.
.infobox {
position: fixed;
position-anchor: --my-anchor;
bottom: anchor(top);
margin-bottom: 10px;
justify-self: anchor-center;
position-try-fallbacks: --custom-bottom;
}
마지막으로 간단한 JavaScript를 얹어서 라디오 버튼에 change 이벤트 핸들러를 달아줍니다. 사용자가 라디오 버튼을 꾹 누를 때마다 그 값이 infobox의 position-try-order 속성에 즉각 반영됩니다.
const infobox = document.querySelector(".infobox");
const radios = document.querySelectorAll('[name="position-try-order"]');
for (const radio of radios) {
radio.addEventListener("change", setTryOrder);
}
function setTryOrder(e) {
const tryOrder = e.target.value;
infobox.style.positionTryOrder = tryOrder;
}
직접 코드를 실행해 보시고 most-height 옵션을 선택해 보세요. 이 옵션을 켜는 순간, 화면 상단보다 하단 쪽에 빈 공간(높이)이 훨씬 더 많다는 걸 브라우저가 눈치채고, 알아서 --custom-bottom 폴백 옵션을 당겨와 툴팁을 앵커 아래쪽으로 부드럽게 옮겨준답니다. 참 똑똑하죠?
상황에 따라 앵커 포지셔닝된 요소를 아예 화면에서 감춰버리고 싶을 때도 분명 있을 겁니다. 대표적인 예로, 사용자가 스크롤을 너무 많이 해서 앵커 요소가 뷰포트 가장자리에 잘려나가 절반쯤 사라졌을 때, 애매하게 붙어있는 툴팁까지 함께 숨겨버리고 싶은 경우가 그렇습니다. 이럴 때 position-visibility 속성을 사용하면 특정 조건에서 연결된 요소를 감쪽같이 숨길 수 있습니다.
기본적으로는 always 값으로 설정되어 있어서 요소가 항상 화면에 끈질기게 표시됩니다. 하지만 no-overflow 값을 주면 요소가 자신을 감싸고 있는 컨테이너나 뷰포트 밖으로 조금이라도 넘치려고 할 때, 브라우저가 그 요소를 강력하게 숨겨버립니다(strongly hide).
반면 anchors-visible이라는 값도 아주 매력적입니다. 이 녀석은 기준점이 되는 앵커가 컨테이너 밖으로 넘어가거나 다른 요소에 완전히 가려져서 우리 눈에서 '완전히(completely)' 사라졌을 때 툴팁 요소를 강력하게 숨겨줍니다. 즉, 앵커의 끄트머리라도 살짝 보인다면 툴팁 요소도 꿋꿋이 화면에 나타나지만, 앵커가 아예 보이지 않게 되면 미련 없이 함께 사라지는 의리 있는 옵션입니다.
여기서 "강력하게 숨겨진다(strongly hidden)"라는 말은, 그 요소와 하위 자식 요소들이 실제 CSS의 visibility 속성이 어떻게 세팅되어 있든 상관없이 강제로 visibility: hidden이 적용된 것처럼 투명 인간 취급을 받는다는 의미입니다.
이 속성이 실제로 어떻게 동작하는지 눈으로 확인해 보시죠!
이전 예제들과 같은 HTML과 CSS 뼈대를 가져오되, 이번엔 infobox를 앵커의 아랫부분 가장자리에 묶어 두었습니다. 그리고 infobox에 야심 차게 position-visibility: no-overflow; 속성을 주었습니다. 이렇게 하면 사용자가 위쪽으로 스크롤을 올려서 infobox가 화면(뷰포트) 위쪽 경계를 넘어서는 순간, 미련 없이 완전히 화면에서 사라지게 됩니다.
<p>
Malesuada nunc vel risus commodo viverra maecenas accumsan lacus. Vel elit
scelerisque mauris pellentesque pulvinar pellentesque habitant morbi
tristique.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Dui nunc mattis enim ut tellus
elementum sagittis vitae et.
</p>
<div class="anchor">⚓︎</div>
<div class="infobox">
<p>This is an information box.</p>
</div>
<p>
Nisi quis eleifend quam adipiscing vitae proin sagittis nisl rhoncus. In arcu
cursus euismod quis viverra nibh cras pulvinar. Vulputate ut pharetra sit amet
aliquam.
</p>
<p>
Malesuada nunc vel risus commodo viverra maecenas accumsan lacus. Vel elit
scelerisque mauris pellentesque pulvinar pellentesque habitant morbi
tristique. Porta lorem mollis aliquam ut porttitor. Turpis cursus in hac
habitasse platea dictumst quisque. Dolor sit amet consectetur adipiscing elit.
Ornare lectus sit amet est placerat. Nulla aliquet porttitor lacus luctus
accumsan.
</p>
.anchor {
font-size: 1.8rem;
color: white;
text-shadow: 1px 1px 1px black;
background-color: hsl(240 100% 75%);
width: fit-content;
border-radius: 10px;
border: 1px solid black;
padding: 3px;
}
.anchor {
anchor-name: --my-anchor;
}
body {
width: 50%;
margin: 0 auto;
}
.infobox {
color: darkblue;
background-color: azure;
border: 1px solid #dddddd;
padding: 10px;
border-radius: 10px;
font-size: 1rem;
}
.infobox {
position: fixed;
position-anchor: --my-anchor;
margin-bottom: 5px;
position-area: top span-all;
position-visibility: no-overflow;
}
페이지를 아래쪽으로 쭉 스크롤해 보세요. 툴팁 요소가 뷰포트의 최상단 모서리에 도달해 넘치려는 찰나, 깔끔하게 화면에서 사라지는 마법을 목격하실 수 있습니다!
💡 강사님의 꿀팁:
position-visibility는 사용자의 시야를 어지럽히지 않도록 깔끔한 UI를 구성하는 숨은 공신입니다. 보통 헤더 아래로 앵커가 말려 들어갈 때, 연관된 툴팁이나 메뉴가 헤더 위를 흉측하게 침범하는 경우가 많은데, 이 속성 하나면 그런 UI 버그를 단번에 해결할 수 있습니다!
조금 더 깊은 학습을 원하신다면 아래 문서들을 곁들여 보세요.