position: sticky는 상자를 흐름 속 원래 자리에 두었다가, 페이지를 굴려 top으로 정한 경계에 닿으면 그 자리에 붙여 두는 값입니다. 붙어 있는 범위는 그 상자를 담은 상자 안까지이고, 겹친 상자 가운데 무엇이 위에 그려질지는 z-index가 정합니다.
레시피 목록을 한참 내려도 머리글이 위에 남아 있으면 다른 페이지로 옮겨 가기 쉽습니다. fixed로 붙이면 머리글이 흐름에서 빠져 첫 카드가 그 밑에 깔립니다. sticky는 머리글의 원래 자리를 남긴 채 붙습니다. 그런데 붙은 머리글 위로 카드가 지나가는 일이 생깁니다 — 이때는 쌓는 차례를 정합니다.
그림 준비 중
1자리를 남긴 채 경계에 붙는다
<div class="head">머리글</div>
<div class="main">본문</div>
body {
margin: 0;
}
.head {
position: sticky;
top: 0;
height: 40px;
background-color: #005bb5;
}
.main {
height: 1600px;
}.head의 창 위 자리0px.head의 위 자리400px.main의 위 자리40px
페이지를 400px 내린 뒤 잰 값입니다. 머리글은 창 맨 위 0에 붙어 있고, 페이지에서 재면 400까지 따라 내려왔습니다. top: 0은 '창 맨 위에서 0인 경계에 닿으면 붙으라'는 뜻입니다. 결과 화면은 안에서 굴러가지 않습니다 — 굴리는 일은 위 실험대가 보이고, 내 파일에서 해 봅니다.
본문 .main은 페이지 위 40에서 시작합니다. sticky 머리글은 흐름에서 빠지지 않아 원래 자리 40을 남깁니다. absolute · fixed라면 본문이 0으로 올라와 머리글 밑에 깔렸을 것입니다(브라우저 규칙 8).
2담은 상자 안에서만 붙는다
담은 상자는 그 요소를 담은 부모의 상자입니다. sticky는 담은 상자 밖으로 나가지 않습니다. 아래 머리글은 높이 240인 .box 안에 들었습니다.
<div class="top"></div>
<div class="box">
<div class="head">머리글</div>
</div>
body {
margin: 0;
height: 1200px;
}
.top {
height: 100px;
}
.box {
height: 240px;
}.head {
position: sticky;
top: 0;
height: 40px;
}.box의 창 위 자리0px.head의 창 위 자리0px
100 내리면 .box의 위 끝이 창 맨 위에 닿고, 머리글도 창 맨 위 0에 붙습니다. 같은 코드를 320 내립니다.
/* 같은 코드, 320px 내림 */.box의 창 위 자리-220px.head의 창 위 자리-20px
320 내리면 .box의 아래 끝이 창 위 20에 와 있어, 높이 40인 머리글은 그 끝에 맞춰 −20으로 같이 올라갑니다. 담은 상자 끝을 넘지 않으려는 것입니다.
담은 상자는 앞 레슨의 기준 상자와 다릅니다. absolute가 자리를 재는 기준 상자는 static이 아닌 가장 가까운 조상이고, sticky가 머무는 담은 상자는 바로 위 부모입니다.
3겹치는 차례 — z-index
position을 준 상자끼리 겹치면, 처음에는 나무에서 뒤에 나온 상자가 위에 그려집니다. 아래 카드는 배지의 기준 상자라 relative입니다.
<div class="head">머리글</div>
<div class="card">카드</div>
<div class="card">카드</div>
<div class="card">카드</div>
<div class="card">카드</div>
body {
margin: 0;
}
.head {
position: sticky;
top: 0;
height: 40px;
background-color: #005bb5;
}
.card {
position: relative;
height: 300px;
}.head의 맨 위 요소div.card
400 내린 뒤 머리글 가운데에 맨 위로 그려진 것은 카드입니다. 카드가 나무에서 뒤에 나와, 붙은 머리글 위로 지나갑니다.
.head {
z-index: 10;
}.head의 맨 위 요소div.head.card의z-indexauto
z-index는 겹친 상자의 쌓는 차례를 정하는 CSS 속성입니다. 숫자가 큰 쪽이 위입니다. 카드는 처음 값 auto(차례 0)라, 10인 머리글이 카드 위로 올라왔습니다. 이 과목에서 z-index는 position을 준 상자에만 씁니다.
예제
링크로 옮긴 제목이 머리글에 가릴 때
레시피 상세 페이지 머리글에 '조리 순서로' 링크가 있습니다. 링크를 누르면 화면이 '조리 순서' 제목으로 옮겨 갑니다. 아래 잰 값은 링크를 누른 뒤 잰 것입니다. 결과 화면을 펴기 전에, 옮겨 간 제목이 보일지 먼저 적어 보세요.
<header class="site-header">
<a href="#steps">조리 순서로</a>
</header>
<p class="intro">긴 소개</p>
<h2 id="steps">조리 순서</h2>
<p class="rest">긴 순서</p>
.site-header {
position: sticky;
top: 0;
padding: 16px;
background-color: #ffffff;
}
/* 긴 글 대신 높이만 줌 */
.intro,
.rest {
height: 1000px;
}
결과 화면 보기
h2의 맨 위 요소header.site-header
링크는 제목을 창 맨 위로 옮기는데, 그 자리에는 붙은 머리글이 있습니다. 제목 가운데의 맨 위 요소가 머리글이라 제목이 가렸습니다.
예상과 다르면 그곳이 배울 곳입니다.
h2 {
scroll-margin-top: 5rem;
}h2의 맨 위 요소h2#steps
scroll-margin-top은 링크로 옮겨 갈 때 그 요소 위에 남길 거리입니다. 머리글 높이보다 넉넉히 남기면 제목이 머리글 아래에 보입니다. 머리글 높이는 글자 크기를 따라 바뀌니 남길 거리도 rem으로 적습니다(5rem은 브라우저 글자 크기 설정이 처음 그대로일 때 80px).
자주 틀리는 곳
틀린 생각: position: sticky만 주면 머리글이 위에 붙는다.
<div class="head">머리글</div>
<div class="main">본문</div>
body {
margin: 0;
}
.head {
position: sticky;
height: 40px;
}
.main {
height: 1600px;
}.head의 창 위 자리-400px
top이 없어 붙을 경계가 없습니다. 머리글은 페이지와 함께 400 올라가 창 밖으로 나갔습니다. 경계를 적었는데도 안 붙으면 담은 상자가 머리글만큼 짧은지 봅니다(2절).
맞는 규칙: sticky에는 top 같은 경계를 함께 적습니다. 붙는 범위는 담은 상자 안이라, 머리글을 짧은 부모로 감싸지 않습니다.
틀린 생각: z-index는 어느 상자에 줘도 차례를 바꾼다.
<div class="a">A</div>
<div class="b">B</div>
.a {
z-index: 10;
height: 100px;
background-color: #005bb5;
}
.b {
position: relative;
top: -50px;
height: 100px;
background-color: #d2d2d7;
}.a의positionstatic.a의 맨 위 요소div.b
B가 50 올라와 A의 아래 절반을 덮었습니다. A에 준 z-index: 10은 A가 static이라 쓰이지 않아, B가 위에 그려졌습니다.
맞는 규칙: z-index는 position을 준 상자에만 씁니다. A에 position: relative를 더하면 10이 쓰여 A가 위로 옵니다.
틀린 생각: z-index를 아주 크게 주면 무엇보다도 위에 그려진다.
<div class="head">머리글</div>
<div class="card">
<span class="badge">한식</span>
</div>
.head {
position: relative;
z-index: 2;
height: 40px;
background-color: #005bb5;
}
.card {
position: relative;
z-index: 1;
height: 100px;
}.badge {
position: absolute;
top: -20px;
z-index: 9999;
background-color: #ffffff;
}.badge의 맨 위 요소div.head
배지는 카드 위 20만큼 머리글 쪽으로 올라갔고, z-index는 9999입니다. 그래도 머리글이 위에 그려졌습니다. 카드처럼 z-index에 숫자를 준 상자는 제 안의 상자들을 한 묶음으로 쌓습니다 — 이 묶음을 쌓임 맥락이라고 합니다. 배지의 9999는 카드 묶음 안에서만 견주고, 묶음 밖에서는 카드의 1이 머리글의 2와 견줍니다.
맞는 규칙: 차례는 같은 묶음 안에서만 견줍니다. 큰 숫자를 쌓아 올리지 말고, 겹치는 두 상자(여기서는 카드와 머리글)의 차례를 정합니다.
확인 문제
1. 2절 예제에서 .box의 높이를 240px에서 400px로 늘렸습니다. 320px 내린 뒤 머리글의 창 위 자리는 몇 px일까요?
<div class="top"></div>
<div class="box">
<div class="head">머리글</div>
</div>
body {
margin: 0;
height: 1200px;
}
.top {
height: 100px;
}
.box {
height: 400px;
}
.head {
position: sticky;
top: 0;
height: 40px;
}힌트
320 내렸을 때 .box의 아래 끝이 창 위 몇 px에 오는지부터 셈해 봅니다.
정답
0px입니다.
.box의 창 위 자리-220px.head의 창 위 자리0px
.box가 100에서 500까지라, 320 내리면 아래 끝이 창 위 180에 있습니다. 머리글 40이 들어갈 자리가 남아 창 맨 위 0에 붙어 있습니다.- 흔한 오답은 −20px입니다. 담은 상자가 길어진 것을 셈에 넣지 않은 것입니다.
2. 레시피 목록 페이지에 카드 그림 위 배지와 붙는 머리글을 함께 둡니다. 겹치기 하위 목표 차례대로 (가)~(라)를 늘어놓으세요.
/* (가) */
.site-header {
z-index: 10;
}
/* (나) */
.recipe-card {
position: relative;
}
/* (다) */
.recipe-card .badge {
top: 24px;
right: 24px;
}
/* (라) */
.recipe-card .badge {
position: absolute;
}
힌트
겹치기 하위 목표 넷의 이름을 차례로 적고, 규칙마다 어느 하위 목표인지 붙여 봅니다.
정답
(나) → (라) → (다) → (가)입니다.
- 기준 상자 정하기 — (나) 카드에
relative - 겹칠 상자 빼기 — (라) 배지에
absolute - 자리 정하기 — (다) 배지의
top·right - 쌓는 차례 — (가) 머리글에
z-index. 카드가relative라 붙은 머리글 위로 지나가기 때문입니다.
(가)를 맨 앞에 두는 답이 흔합니다. 차례는 겹칠 상자가 다 놓인 뒤 무엇이 위인지 보고 정합니다.
3. 동아리 공지 페이지의 알림 띠입니다. 굴려도 창 맨 위에 붙어 있게 했는데, 내리면 띠가 함께 올라가 사라집니다. 몇 번 줄을 어떻게 고칠까요?
<div class="notice">
<p class="bar">
이번 주 모임은 쉽니다
</p>
</div>
<div class="list">
공지 목록
</div>
.bar {
position: sticky;
top: 0;
margin: 0;
background-color: #ffffff;
}
.list {
height: 1600px;
}
힌트
띠 .bar를 담은 상자가 무엇이고, 그 상자가 얼마나 긴지 봅니다.
힌트 둘
담은 상자가 띠만큼 짧으면 띠가 움직일 범위가 없습니다(2절).
정답
css 1번 줄의 선택자 .bar를 .notice로 바꿉니다. 고치기 전 띠를 400px 내린 뒤 재면 이렇습니다.
.bar의 창 위 자리-392px
고친 뒤입니다.
<div class="notice">
<p class="bar">
이번 주 모임은 쉽니다
</p>
</div>
<div class="list">
공지 목록
</div>
.notice {
position: sticky;
top: 0;
margin: 0;
background-color: #ffffff;
}
.list {
height: 1600px;
}.bar의 창 위 자리0px
- 띠를 담은
.notice는 띠 하나만 들어 띠와 높이가 같습니다. 띠가 붙을 범위가 없어 페이지와 함께 올라갔습니다. sticky를.notice에 주면 담은 상자가 페이지 전체(body)라 끝까지 붙어 있습니다. 흔한 오답은top: 0을top: 1px처럼 바꾸는 답입니다. 경계가 아니라 범위가 문제입니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
1절 예제를 index.html과 style.css로 저장하고 link로 이어 엽니다(문서 뼈대의 body 안에 예제 HTML을 넣습니다). 페이지를 내려 머리글이 붙는지 본 뒤, top: 0; 줄을 지우면 어떻게 될지 먼저 적고 저장 · 새로고침해 다시 내려 봅니다.