본문으로 건너뛰기

sticky 머리글과 z-index

HTML·CSS 목차겹치고 고정하기: position레슨 78 / 93

목차

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;
}
힌트

겹치기 하위 목표 넷의 이름을 차례로 적고, 규칙마다 어느 하위 목표인지 붙여 봅니다.

정답

(나) → (라) → (다) → (가)입니다.

  1. 기준 상자 정하기 — (나) 카드에 relative
  2. 겹칠 상자 빼기 — (라) 배지에 absolute
  3. 자리 정하기 — (다) 배지의 top · right
  4. 쌓는 차례 — (가) 머리글에 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; 줄을 지우면 어떻게 될지 먼저 적고 저장 · 새로고침해 다시 내려 봅니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

HTML·CSS 목차로