CSS position 완전 정복 강의 자료 만들기

소개

학생들이 제일 어려워하는 position에 대해서 어떻게 강의를 하면 좋을지 고민하던 중 만들게 되었습니다.

진행 방법

어떤 도구를 사용했고, 어떻게 활용하셨나요?

claude를 사용했습니다. 컴퓨터를 이리저리 옮겨 다니면서 자료를 만들다보니 claude code를 사용하지 못 했습니다.

Tip: 사용한 프롬프트 전문을 꼭 포함하고, 내용을 짧게 소개해 주세요.

한국어로 된 문자 메시지의 스크린샷

Tip: 활용 이미지나 캡처 화면을 꼭 남겨주세요.

위치

Tip: 코드 전문은 코드블록에 감싸서 작성해주세요. ( / 을 눌러 '코드 블록'을 선택)


<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>CSS Position 완전 정복</title>
  <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700;900&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"/>
  <style>
    :root {
      --bg: #111318;
      --surface: #1a1d24;
      --surface2: #20242e;
      --border: #2c3040;
      --text: #e2e6f0;
      --muted: #6b728a;

      --static-c:  #94a3b8;
      --relative-c:#60a5fa;
      --absolute-c:#f472b6;
      --fixed-c:   #34d399;
      --sticky-c:  #fbbf24;

      --static-bg:  rgba(148,163,184,0.08);
      --relative-bg:rgba(96,165,250,0.08);
      --absolute-bg:rgba(244,114,182,0.08);
      --fixed-bg:   rgba(52,211,153,0.08);
      --sticky-bg:  rgba(251,191,36,0.08);

      --static-bd:  rgba(148,163,184,0.25);
      --relative-bd:rgba(96,165,250,0.25);
      --absolute-bd:rgba(244,114,182,0.25);
      --fixed-bd:   rgba(52,211,153,0.25);
      --sticky-bd:  rgba(251,191,36,0.25);
    }

    *{box-sizing:border-box;margin:0;padding:0;}

    html { scroll-behavior: smooth; }

    body {
      background: var(--bg);
      color: var(--text);
      font-family: 'Noto Sans KR', sans-serif;
      line-height: 1.7;
      overflow-x: hidden;
    }

    /* 배경 노이즈 */
    body::before {
      content:'';
      position:fixed;
      inset:0;
      background-image:
        radial-gradient(circle at 15% 20%, rgba(96,165,250,0.06) 0%, transparent 40%),
        radial-gradient(circle at 85% 70%, rgba(244,114,182,0.06) 0%, transparent 40%),
        radial-gradient(circle at 50% 50%, rgba(52,211,153,0.04) 0%, transparent 50%);
      pointer-events:none;
      z-index:0;
    }

    /* ── 히어로 ── */
    .hero {
      position: relative;
      z-index: 1;
      padding: 80px 24px 72px;
      text-align: center;
      border-bottom: 1px solid var(--border);
    }

    .hero-kicker {
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px;
      letter-spacing: .25em;
      text-transform: uppercase;
      color: var(--muted);
      margin-bottom: 20px;
    }

    .hero h1 {
      font-size: clamp(2.4rem,6vw,4rem);
      font-weight: 900;
      letter-spacing: -.04em;
      line-height: 1.05;
      margin-bottom: 20px;
    }

    .hero h1 .word {
      display: inline-block;
      transition: color .2s;
    }
    .hero h1 .w1 { color: var(--relative-c); }
    .hero h1 .w2 { color: var(--absolute-c); }
    .hero h1 .w3 { color: var(--fixed-c); }
    .hero h1 .w4 { color: var(--sticky-c); }

    .hero-sub {
      font-size: 15px;
      color: var(--muted);
      max-width: 520px;
      margin: 0 auto 32px;
      line-height: 1.85;
    }

    /* 빠른 이동 네비 */
    .quick-nav {
      display: flex;
      justify-content: center;
      gap: 8px;
      flex-wrap: wrap;
    }

    .qn-btn {
      padding: 7px 16px;
      border-radius: 20px;
      border: 1px solid;
      background: transparent;
      font-family: 'JetBrains Mono', monospace;
      font-size: 12px;
      cursor: pointer;
      transition: all .2s;
      text-decoration: none;
    }

    .qn-static   { border-color: var(--static-bd);   color: var(--static-c); }
    .qn-relative { border-color: var(--relative-bd);  color: var(--relative-c); }
    .qn-absolute { border-color: var(--absolute-bd);  color: var(--absolute-c); }
    .qn-fixed    { border-color: var(--fixed-bd);     color: var(--fixed-c); }
    .qn-sticky   { border-color: var(--sticky-bd);    color: var(--sticky-c); }

    .qn-btn:hover { filter: brightness(1.3); transform: translateY(-2px); }

    /* ── 래퍼 ── */
    .wrapper {
      position: relative;
      z-index: 1;
      max-width: 920px;
      margin: 0 auto;
      padding: 0 24px 100px;
    }

    /* ── 섹션 헤더 ── */
    .section { margin-top: 72px; }

    .section-header {
      display: flex;
      align-items: center;
      gap: 16px;
      margin-bottom: 24px;
      padding-bottom: 20px;
      border-bottom: 1px solid var(--border);
    }

    .pos-badge {
      font-family: 'JetBrains Mono', monospace;
      font-size: 13px;
      font-weight: 700;
      padding: 6px 18px;
      border-radius: 8px;
      border: 1.5px solid;
      letter-spacing: .05em;
    }

    .badge-static   { background:var(--static-bg);  border-color:var(--static-bd);  color:var(--static-c); }
    .badge-relative { background:var(--relative-bg); border-color:var(--relative-bd); color:var(--relative-c); }
    .badge-absolute { background:var(--absolute-bg); border-color:var(--absolute-bd); color:var(--absolute-c); }
    .badge-fixed    { background:var(--fixed-bg);   border-color:var(--fixed-bd);   color:var(--fixed-c); }
    .badge-sticky   { background:var(--sticky-bg);  border-color:var(--sticky-bd);  color:var(--sticky-c); }

    .section-title-wrap h2 {
      font-size: 1.5rem;
      font-weight: 800;
      letter-spacing: -.02em;
      margin-bottom: 2px;
    }

    .section-title-wrap p {
      font-size: 13px;
      color: var(--muted);
    }

    /* ── 설명 카드 ── */
    .desc-card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 14px;
      padding: 22px 26px;
      margin-bottom: 18px;
      font-size: 14.5px;
      line-height: 1.9;
      color: #c5cce0;
    }

    .desc-card strong { color: var(--text); }

    .key-point {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      margin-top: 14px;
      padding: 10px 16px;
      border-radius: 8px;
      font-size: 13.5px;
      font-weight: 600;
    }

    .kp-static   { background:var(--static-bg);  border:1px solid var(--static-bd);  color:var(--static-c); }
    .kp-relative { background:var(--relative-bg); border:1px solid var(--relative-bd); color:var(--relative-c); }
    .kp-absolute { background:var(--absolute-bg); border:1px solid var(--absolute-bd); color:var(--absolute-c); }
    .kp-fixed    { background:var(--fixed-bg);   border:1px solid var(--fixed-bd);   color:var(--fixed-c); }
    .kp-sticky   { background:var(--sticky-bg);  border:1px solid var(--sticky-bd);  color:var(--sticky-c); }

    /* ── 코드 블록 ── */
    .code-block {
      background: #0d0f14;
      border: 1px solid var(--border);
      border-radius: 12px;
      overflow: hidden;
      margin-bottom: 18px;
    }

    .cb-bar {
      display: flex;
      align-items: center;
      gap: 7px;
      padding: 10px 16px;
      background: rgba(255,255,255,0.03);
      border-bottom: 1px solid var(--border);
    }

    .dot{width:10px;height:10px;border-radius:50%;}
    .dr{background:#f38ba8;}.dy{background:#f9e2af;}.dg{background:#a6e3a1;}

    .cb-lang {
      font-family:'JetBrains Mono',monospace;
      font-size:11px;color:var(--muted);
      margin-left:auto;letter-spacing:.1em;
    }

    pre {
      padding: 20px 22px;
      font-family: 'JetBrains Mono', monospace;
      font-size: 13px;
      line-height: 2;
      overflow-x: auto;
      color: #cdd6f4;
    }

    .kw  { color: #cba6f7; }
    .pr  { color: #89b4fa; }
    .vl  { color: #a6e3a1; }
    .vl2 { color: #fab387; }
    .cm  { color: #45475a; font-style: italic; }
    .sel { color: #89dceb; }
    .hl-static   { color: var(--static-c);   font-weight:600; }
    .hl-relative { color: var(--relative-c);  font-weight:600; }
    .hl-absolute { color: var(--absolute-c);  font-weight:600; }
    .hl-fixed    { color: var(--fixed-c);    font-weight:600; }
    .hl-sticky   { color: var(--sticky-c);   font-weight:600; }

    /* ── 인터랙티브 데모 ── */
    .demo-wrap {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 14px;
      overflow: hidden;
      margin-bottom: 18px;
    }

    .demo-header {
      padding: 12px 18px;
      background: rgba(255,255,255,0.03);
      border-bottom: 1px solid var(--border);
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px;
      color: var(--muted);
      letter-spacing: .12em;
      text-transform: uppercase;
    }

    .demo-body { padding: 24px; }

    /* static 데모 */
    .demo-static-flow {
      display: flex;
      flex-direction: column;
      gap: 6px;
    }

    .flow-box {
      padding: 12px 18px;
      border-radius: 8px;
      font-family: 'JetBrains Mono', monospace;
      font-size: 13px;
      background: var(--static-bg);
      border: 1px solid var(--static-bd);
      color: var(--static-c);
    }

    /* relative 데모 */
    .demo-relative-area {
      position: relative;
      height: 120px;
      background: var(--surface2);
      border-radius: 10px;
      display: flex;
      align-items: center;
      padding: 0 20px;
      gap: 10px;
    }

    .rel-box-normal {
      padding: 10px 16px;
      border-radius: 6px;
      background: rgba(100,116,139,0.2);
      border: 1px dashed #475569;
      color: #64748b;
      font-family: 'JetBrains Mono', monospace;
      font-size: 12px;
    }

    .rel-box-moved {
      position: relative;
      top: -20px;
      left: 10px;
      padding: 10px 16px;
      border-radius: 6px;
      background: var(--relative-bg);
      border: 2px solid var(--relative-bd);
      color: var(--relative-c);
      font-family: 'JetBrains Mono', monospace;
      font-size: 12px;
      font-weight: 600;
    }

    .rel-ghost {
      position: relative;
      padding: 10px 16px;
      border-radius: 6px;
      border: 1px dashed var(--relative-bd);
      color: rgba(96,165,250,0.3);
      font-family: 'JetBrains Mono', monospace;
      font-size: 12px;
    }

    /* absolute 데모 */
    .demo-absolute-area {
      position: relative;
      height: 160px;
      background: var(--surface2);
      border-radius: 10px;
      border: 2px dashed #475569;
    }

    .abs-parent-label {
      position: absolute;
      top: 8px; left: 12px;
      font-family: 'JetBrains Mono', monospace;
      font-size: 10px;
      color: #475569;
      letter-spacing: .1em;
    }

    .abs-box {
      position: absolute;
      padding: 10px 16px;
      border-radius: 6px;
      background: var(--absolute-bg);
      border: 2px solid var(--absolute-bd);
      color: var(--absolute-c);
      font-family: 'JetBrains Mono', monospace;
      font-size: 12px;
      font-weight: 600;
    }

    /* fixed 데모 — 미니 뷰포트 시뮬레이션 */
    .demo-fixed-viewport {
      position: relative;
      height: 180px;
      background: var(--surface2);
      border-radius: 10px;
      border: 2px solid var(--border);
      overflow: hidden;
    }

    .vp-label {
      position: absolute;
      top: 8px; left: 12px;
      font-family: 'JetBrains Mono', monospace;
      font-size: 10px;
      color: var(--muted);
      letter-spacing: .1em;
    }

    .vp-scroll-content {
      padding: 40px 20px 20px;
      overflow-y: auto;
      height: 100%;
    }

    .vp-text-line {
      height: 14px;
      border-radius: 4px;
      background: rgba(255,255,255,0.06);
      margin-bottom: 10px;
    }

    .fixed-sim-box {
      position: absolute;
      bottom: 14px; right: 14px;
      padding: 8px 14px;
      border-radius: 8px;
      background: var(--fixed-bg);
      border: 2px solid var(--fixed-bd);
      color: var(--fixed-c);
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px;
      font-weight: 700;
      animation: fixedPulse 2s ease-in-out infinite;
    }

    @keyframes fixedPulse {
      0%,100% { box-shadow: 0 0 0 0 rgba(52,211,153,0.2); }
      50%      { box-shadow: 0 0 0 8px rgba(52,211,153,0); }
    }

    /* sticky 데모 */
    .demo-sticky-area {
      height: 200px;
      overflow-y: auto;
      background: var(--surface2);
      border-radius: 10px;
      border: 1px solid var(--border);
    }

    .sticky-header {
      position: sticky;
      top: 0;
      background: var(--sticky-bg);
      border-bottom: 2px solid var(--sticky-bd);
      color: var(--sticky-c);
      font-family: 'JetBrains Mono', monospace;
      font-size: 13px;
      font-weight: 700;
      padding: 12px 18px;
      z-index: 10;
    }

    .sticky-content-item {
      padding: 14px 18px;
      border-bottom: 1px solid rgba(255,255,255,0.04);
      font-size: 13px;
      color: var(--muted);
    }

    /* ── 비교표 ── */
    .compare-table {
      width: 100%;
      border-collapse: collapse;
      font-size: 13.5px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 14px;
      overflow: hidden;
    }

    .compare-table th {
      padding: 14px 16px;
      text-align: left;
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px;
      letter-spacing: .12em;
      text-transform: uppercase;
      color: var(--muted);
      background: var(--surface2);
      border-bottom: 1px solid var(--border);
    }

    .compare-table td {
      padding: 14px 16px;
      border-bottom: 1px solid rgba(255,255,255,0.04);
      color: #b0b8d0;
      vertical-align: middle;
    }

    .compare-table tr:last-child td { border-bottom: none; }
    .compare-table tr:hover td { background: rgba(255,255,255,0.02); }

    .compare-table td:first-child {
      font-family: 'JetBrains Mono', monospace;
      font-weight: 700;
      font-size: 13px;
    }

    .ct-static   td:first-child { color: var(--static-c); }
    .ct-relative td:first-child { color: var(--relative-c); }
    .ct-absolute td:first-child { color: var(--absolute-c); }
    .ct-fixed    td:first-child { color: var(--fixed-c); }
    .ct-sticky   td:first-child { color: var(--sticky-c); }

    .tc-yes { color: #86efac; font-family:'JetBrains Mono',monospace;font-size:12px; }
    .tc-no  { color: #fca5a5; font-family:'JetBrains Mono',monospace;font-size:12px; }
    .tc-cond{ color: #fde68a; font-family:'JetBrains Mono',monospace;font-size:12px; }

    /* ── 핵심 정리 카드 ── */
    .summary-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
      gap: 14px;
      margin-top: 56px;
    }

    .sum-card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 14px;
      padding: 20px;
      transition: transform .2s;
    }

    .sum-card:hover { transform: translateY(-4px); }

    .sum-card .sc-dot {
      width: 10px; height: 10px;
      border-radius: 50%;
      margin-bottom: 12px;
    }

    .sc-static   .sc-dot { background: var(--static-c); }
    .sc-relative .sc-dot { background: var(--relative-c); }
    .sc-absolute .sc-dot { background: var(--absolute-c); }
    .sc-fixed    .sc-dot { background: var(--fixed-c); }
    .sc-sticky   .sc-dot { background: var(--sticky-c); }

    .sum-card .sc-name {
      font-family: 'JetBrains Mono', monospace;
      font-size: 14px;
      font-weight: 700;
      margin-bottom: 8px;
    }

    .sc-static   .sc-name { color: var(--static-c); }
    .sc-relative .sc-name { color: var(--relative-c); }
    .sc-absolute .sc-name { color: var(--absolute-c); }
    .sc-fixed    .sc-name { color: var(--fixed-c); }
    .sc-sticky   .sc-name { color: var(--sticky-c); }

    .sum-card .sc-desc {
      font-size: 12.5px;
      color: var(--muted);
      line-height: 1.7;
    }

    .sum-card .sc-when {
      margin-top: 10px;
      font-size: 11.5px;
      color: var(--muted);
      border-top: 1px solid var(--border);
      padding-top: 10px;
    }

    /* ── 애니메이션 ── */
    @keyframes fadeUp {
      from { opacity:0; transform:translateY(24px); }
      to   { opacity:1; transform:translateY(0); }
    }

    .section { animation: fadeUp .55s ease both; }
    .section:nth-child(1) { animation-delay:.05s; }
    .section:nth-child(2) { animation-delay:.1s; }
    .section:nth-child(3) { animation-delay:.15s; }
    .section:nth-child(4) { animation-delay:.2s; }
    .section:nth-child(5) { animation-delay:.25s; }
    .section:nth-child(6) { animation-delay:.3s; }
    .section:nth-child(7) { animation-delay:.35s; }

    /* 반응형 */
    @media (max-width:600px) {
      .compare-table th:nth-child(4),
      .compare-table td:nth-child(4) { display:none; }
    }

    /* 데모 토글 버튼 */
    .demo-toggle-row {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      margin-bottom: 14px;
    }

    .demo-toggle {
      padding: 6px 14px;
      border-radius: 6px;
      border: 1px solid var(--border);
      background: transparent;
      color: var(--muted);
      font-family: 'JetBrains Mono', monospace;
      font-size: 12px;
      cursor: pointer;
      transition: all .2s;
    }

    .demo-toggle.on {
      background: var(--relative-bg);
      border-color: var(--relative-bd);
      color: var(--relative-c);
    }

    /* absolute 위치 컨트롤 */
    .abs-controls {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      margin-top: 12px;
    }

    .abs-ctrl-btn {
      padding: 6px 14px;
      border-radius: 6px;
      border: 1px solid var(--absolute-bd);
      background: var(--absolute-bg);
      color: var(--absolute-c);
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px;
      cursor: pointer;
      transition: all .15s;
    }

    .abs-ctrl-btn:hover { filter: brightness(1.3); }
    .abs-ctrl-btn.active {
      background: rgba(244,114,182,0.25);
      font-weight: 700;
    }
  </style>
</head>
<body>

<!-- 히어로 -->
<div class="hero">
  <div class="hero-kicker">CSS · Layout · Positioning</div>
  <h1>
    <span class="word w1">position</span>
    <span style="color:var(--muted);font-weight:300;"> 완전 정복</span>
  </h1>
  <p class="hero-sub">
    static · relative · absolute · fixed · sticky<br/>
    5가지 포지션을 인터랙티브 예제와 함께 완벽히 이해해요!
  </p>
  <div class="quick-nav">
    <a href="#sec-static"   class="qn-btn qn-static">static</a>
    <a href="#sec-relative" class="qn-btn qn-relative">relative</a>
    <a href="#sec-absolute" class="qn-btn qn-absolute">absolute</a>
    <a href="#sec-fixed"    class="qn-btn qn-fixed">fixed</a>
    <a href="#sec-sticky"   class="qn-btn qn-sticky">sticky</a>
  </div>
</div>

<div class="wrapper">

  <!-- ─── STATIC ─── -->
  <div class="section" id="sec-static">
    <div class="section-header">
      <div class="pos-badge badge-static">static</div>
      <div class="section-title-wrap">
        <h2>기본값 — 흐름대로</h2>
        <p>position을 지정하지 않으면 기본 적용되는 값</p>
      </div>
    </div>

    <div class="desc-card">
      <strong>position: static</strong>은 모든 요소의 기본값이에요.<br/>
      HTML 문서의 <strong>일반 흐름(Normal Flow)</strong>에 따라 위에서 아래, 왼쪽에서 오른쪽으로 차례로 배치됩니다.<br/>
      <code style="font-family:'JetBrains Mono',monospace;background:var(--surface2);padding:2px 7px;border-radius:4px;font-size:13px;">top · right · bottom · left · z-index</code> 속성이 <strong>적용되지 않아요.</strong>
      <br/>
      <div class="key-point kp-static">📌 top/left 효과 없음 · z-index 무시 · 일반 흐름 유지</div>
    </div>

    <div class="code-block">
      <div class="cb-bar"><div class="dot dr"></div><div class="dot dy"></div><div class="dot dg"></div><div class="cb-lang">CSS</div></div>
      <pre><span class="sel">.box</span> {
  <span class="pr">position</span>: <span class="hl-static">static</span>;  <span class="cm">/* 기본값 — 생략 가능 */</span>
  <span class="cm">/* top: 20px;  ← 아무 효과 없음! */</span>
  <span class="cm">/* z-index: 10; ← 아무 효과 없음! */</span>
}</pre>
    </div>

    <div class="demo-wrap">
      <div class="demo-header">🖥 데모 — 일반 흐름으로 순서대로 쌓임</div>
      <div class="demo-body">
        <div class="demo-static-flow">
          <div class="flow-box">Box 1 — position: static (기본)</div>
          <div class="flow-box">Box 2 — position: static (기본)</div>
          <div class="flow-box">Box 3 — position: static (기본)</div>
        </div>
        <p style="font-size:12px;color:var(--muted);margin-top:12px;">↑ 위에서 아래로, 선언 순서대로 배치됩니다.</p>
      </div>
    </div>
  </div>

  <!-- ─── RELATIVE ─── -->
  <div class="section" id="sec-relative">
    <div class="section-header">
      <div class="pos-badge badge-relative">relative</div>
      <div class="section-title-wrap">
        <h2>자기 자신 기준으로 이동</h2>
        <p>원래 있던 자리를 유지하며 상대적으로 이동</p>
      </div>
    </div>

    <div class="desc-card">
      <strong>position: relative</strong>는 요소가 원래 있던 자리(static 위치)를 <strong>기준</strong>으로 이동해요.<br/>
      중요한 점은 <strong>원래 자리가 그대로 유지</strong>된다는 것! 빈 공간이 남아요.<br/>
      <code style="font-family:'JetBrains Mono',monospace;background:var(--surface2);padding:2px 7px;border-radius:4px;font-size:13px;">top · right · bottom · left</code>로 이동 거리를 지정해요.
      <br/>
      <div class="key-point kp-relative">📌 원래 자리 유지 · 자신의 위치 기준 이동 · absolute의 기준점이 됨</div>
    </div>

    <div class="code-block">
      <div class="cb-bar"><div class="dot dr"></div><div class="dot dy"></div><div class="dot dg"></div><div class="cb-lang">CSS</div></div>
      <pre><span class="sel">.box</span> {
  <span class="pr">position</span>: <span class="hl-relative">relative</span>;
  <span class="pr">top</span>:  <span class="vl">-20px</span>;  <span class="cm">/* 원래 자리에서 위로 20px */</span>
  <span class="pr">left</span>: <span class="vl">10px</span>;   <span class="cm">/* 원래 자리에서 오른쪽 10px */</span>
}</pre>
    </div>

    <div class="demo-wrap">
      <div class="demo-header">🖥 데모 — 가운데 박스가 relative로 이동</div>
      <div class="demo-body">
        <div class="demo-relative-area">
          <div class="rel-box-normal">Box 1</div>
          <div style="position:relative;display:inline-flex;flex-direction:column;">
            <div class="rel-ghost">Box 2 (원래 자리)</div>
            <div class="rel-box-moved">Box 2 ↖ relative</div>
          </div>
          <div class="rel-box-normal">Box 3</div>
        </div>
        <p style="font-size:12px;color:var(--muted);margin-top:12px;">↑ Box 2가 이동해도 원래 자리(점선)는 그대로 유지돼요.</p>
      </div>
    </div>
  </div>

  <!-- ─── ABSOLUTE ─── -->
  <div class="section" id="sec-absolute">
    <div class="section-header">
      <div class="pos-badge badge-absolute">absolute</div>
      <div class="section-title-wrap">
        <h2>부모 기준으로 자유롭게 배치</h2>
        <p>가장 가까운 positioned 부모 기준으로 위치 결정</p>
      </div>
    </div>

    <div class="desc-card">
      <strong>position: absolute</strong><strong>일반 흐름에서 완전히 벗어나요.</strong> 빈 자리가 생기지 않아요.<br/>
      <strong>가장 가까운 position이 static이 아닌 부모</strong>를 기준으로 배치돼요.<br/>
      부모 중 해당하는 게 없으면 <strong>브라우저 전체(html)</strong>가 기준이 됩니다.<br/>
      <div class="key-point kp-absolute">📌 흐름 이탈 · relative 부모 기준 · top/left로 정확한 위치 지정</div>
    </div>

    <div class="code-block">
      <div class="cb-bar"><div class="dot dr"></div><div class="dot dy"></div><div class="dot dg"></div><div class="cb-lang">CSS</div></div>
      <pre><span class="cm">/* 부모에 relative 필수! */</span>
<span class="sel">.parent</span> {
  <span class="pr">position</span>: <span class="hl-relative">relative</span>;
}

<span class="sel">.child</span> {
  <span class="pr">position</span>: <span class="hl-absolute">absolute</span>;
  <span class="pr">top</span>:   <span class="vl">20px</span>;   <span class="cm">/* 부모 위쪽 끝에서 20px */</span>
  <span class="pr">right</span>: <span class="vl">20px</span>;   <span class="cm">/* 부모 오른쪽 끝에서 20px */</span>
}</pre>
    </div>

    <div class="demo-wrap">
      <div class="demo-header">🖥 데모 — 부모(점선 박스) 안에서 자유 배치</div>
      <div class="demo-body">
        <div class="abs-controls">
          <button class="abs-ctrl-btn active" id="abs-btn-tr" onclick="moveAbs('tr')">우상단</button>
          <button class="abs-ctrl-btn" id="abs-btn-tl" onclick="moveAbs('tl')">좌상단</button>
          <button class="abs-ctrl-btn" id="abs-btn-br" onclick="moveAbs('br')">우하단</button>
          <button class="abs-ctrl-btn" id="abs-btn-center" onclick="moveAbs('center')">가운데</button>
        </div>
        <div class="demo-absolute-area" style="margin-top:10px;">
          <span class="abs-parent-label">PARENT (position: relative)</span>
          <div class="abs-box" id="abs-box" style="top:20px;right:20px;">
            absolute
          </div>
        </div>
        <p style="font-size:12px;color:var(--muted);margin-top:10px;">↑ 버튼을 눌러 absolute 요소의 위치를 바꿔보세요!</p>
      </div>
    </div>
  </div>

  <!-- ─── FIXED ─── -->
  <div class="section" id="sec-fixed">
    <div class="section-header">
      <div class="pos-badge badge-fixed">fixed</div>
      <div class="section-title-wrap">
        <h2>화면(뷰포트)에 고정</h2>
        <p>스크롤해도 항상 같은 자리에 고정</p>
      </div>
    </div>

    <div class="desc-card">
      <strong>position: fixed</strong><strong>브라우저 뷰포트(화면)</strong>를 기준으로 위치를 고정해요.<br/>
      페이지를 아무리 스크롤해도 <strong>화면에서 움직이지 않아요.</strong><br/>
      내비게이션 바, 플로팅 버튼, 쿠키 배너 같은 요소에 자주 사용해요.<br/>
      <div class="key-point kp-fixed">📌 뷰포트 기준 · 스크롤 무관 고정 · 흐름 이탈 · z-index 필요</div>
    </div>

    <div class="code-block">
      <div class="cb-bar"><div class="dot dr"></div><div class="dot dy"></div><div class="dot dg"></div><div class="cb-lang">CSS</div></div>
      <pre><span class="cm">/* 화면 우하단에 고정 버튼 */</span>
<span class="sel">.float-btn</span> {
  <span class="pr">position</span>: <span class="hl-fixed">fixed</span>;
  <span class="pr">bottom</span>: <span class="vl">20px</span>;
  <span class="pr">right</span>:  <span class="vl">20px</span>;
  <span class="pr">z-index</span>: <span class="vl2">999</span>;  <span class="cm">/* 항상 위에 표시 */</span>
}

<span class="cm">/* 화면 상단 고정 헤더 */</span>
<span class="sel">.navbar</span> {
  <span class="pr">position</span>: <span class="hl-fixed">fixed</span>;
  <span class="pr">top</span>:   <span class="vl">0</span>;
  <span class="pr">left</span>:  <span class="vl">0</span>;
  <span class="pr">right</span>: <span class="vl">0</span>;
}</pre>
    </div>

    <div class="demo-wrap">
      <div class="demo-header">🖥 데모 — 초록 박스는 스크롤해도 항상 고정</div>
      <div class="demo-body">
        <div class="demo-fixed-viewport">
          <span class="vp-label">VIEWPORT (브라우저 화면)</span>
          <div class="vp-scroll-content">
            <div class="vp-text-line" style="width:80%"></div>
            <div class="vp-text-line" style="width:95%"></div>
            <div class="vp-text-line" style="width:70%"></div>
            <div class="vp-text-line" style="width:85%"></div>
            <div class="vp-text-line" style="width:60%"></div>
            <div class="vp-text-line" style="width:90%"></div>
          </div>
          <div class="fixed-sim-box">FIXED ✦</div>
        </div>
        <p style="font-size:12px;color:var(--muted);margin-top:10px;">↑ 스크롤이 발생해도 초록 박스는 항상 같은 위치에 고정돼요.</p>
      </div>
    </div>
  </div>

  <!-- ─── STICKY ─── -->
  <div class="section" id="sec-sticky">
    <div class="section-header">
      <div class="pos-badge badge-sticky">sticky</div>
      <div class="section-title-wrap">
        <h2>스크롤하다 걸리면 고정</h2>
        <p>relative + fixed의 혼합! 임계점에 도달하면 고정</p>
      </div>
    </div>

    <div class="desc-card">
      <strong>position: sticky</strong><strong>relative와 fixed의 혼합</strong>이에요.<br/>
      평소엔 relative처럼 일반 흐름을 따르다가, <strong>스크롤이 지정한 임계점에 도달하면 fixed처럼 고정</strong>돼요.<br/>
      테이블 헤더, 섹션 헤더 등에 자주 사용해요.<br/>
      <strong>주의:</strong> 부모 요소가 <code style="font-family:'JetBrains Mono',monospace;background:var(--surface2);padding:2px 7px;border-radius:4px;font-size:13px;">overflow: hidden</code>이면 작동하지 않아요!
      <br/>
      <div class="key-point kp-sticky">📌 스크롤 임계점 도달 시 고정 · top 값 필수 · 부모 영역 안에서만</div>
    </div>

    <div class="code-block">
      <div class="cb-bar"><div class="dot dr"></div><div class="dot dy"></div><div class="dot dg"></div><div class="cb-lang">CSS</div></div>
      <pre><span class="sel">.sticky-header</span> {
  <span class="pr">position</span>: <span class="hl-sticky">sticky</span>;
  <span class="pr">top</span>: <span class="vl">0</span>;  <span class="cm">/* ← 이 값이 임계점! 필수! */</span>
  <span class="cm">/* top: 0 → 화면 최상단에 닿으면 고정 */</span>
  <span class="cm">/* top: 60px → 위에서 60px 지점에 닿으면 고정 */</span>
  <span class="pr">z-index</span>: <span class="vl2">10</span>;
}</pre>
    </div>

    <div class="demo-wrap">
      <div class="demo-header">🖥 데모 — 아래로 스크롤하면 헤더가 상단에 고정!</div>
      <div class="demo-body">
        <div class="demo-sticky-area">
          <div class="sticky-content-item">섹션 A 내용 · 스크롤을 내려보세요 ↓</div>
          <div class="sticky-content-item">내용 1</div>
          <div class="sticky-content-item">내용 2</div>
          <div class="sticky-header">📌 STICKY HEADER — top: 0 에서 고정!</div>
          <div class="sticky-content-item">섹션 B 내용</div>
          <div class="sticky-content-item">내용 3</div>
          <div class="sticky-content-item">내용 4</div>
          <div class="sticky-content-item">내용 5</div>
          <div class="sticky-content-item">내용 6</div>
          <div class="sticky-content-item">내용 7 · 끝</div>
        </div>
        <p style="font-size:12px;color:var(--muted);margin-top:10px;">↑ 박스 안을 스크롤하면 노란 헤더가 상단에 고정되는 걸 확인하세요!</p>
      </div>
    </div>
  </div>

  <!-- ─── 비교표 ─── -->
  <div class="section">
    <div class="section-header">
      <div class="pos-badge" style="background:rgba(255,255,255,0.05);border-color:var(--border);color:var(--muted);">비교표</div>
      <div class="section-title-wrap">
        <h2>5가지 한눈에 비교</h2>
        <p>어떤 상황에서 무엇을 써야 할까?</p>
      </div>
    </div>

    <div style="overflow-x:auto;">
      <table class="compare-table">
        <thead>
          <tr>
            <th>position</th>
            <th>기준점</th>
            <th>흐름 이탈</th>
            <th>스크롤 고정</th>
            <th>사용 예</th>
          </tr>
        </thead>
        <tbody>
          <tr class="ct-static">
            <td>static</td>
            <td>일반 흐름</td>
            <td><span class="tc-no">❌ 아니오</span></td>
            <td><span class="tc-no"></span></td>
            <td>일반 텍스트, 기본 박스</td>
          </tr>
          <tr class="ct-relative">
            <td>relative</td>
            <td>자기 자신</td>
            <td><span class="tc-no">❌ 아니오</span></td>
            <td><span class="tc-no"></span></td>
            <td>미세 조정, absolute 기준 부모</td>
          </tr>
          <tr class="ct-absolute">
            <td>absolute</td>
            <td>relative 부모</td>
            <td><span class="tc-yes">✅ 예</span></td>
            <td><span class="tc-no"></span></td>
            <td>툴팁, 뱃지, 팝업</td>
          </tr>
          <tr class="ct-fixed">
            <td>fixed</td>
            <td>뷰포트(화면)</td>
            <td><span class="tc-yes">✅ 예</span></td>
            <td><span class="tc-yes">✅ 예</span></td>
            <td>상단 헤더, 플로팅 버튼</td>
          </tr>
          <tr class="ct-sticky">
            <td>sticky</td>
            <td>스크롤 임계점</td>
            <td><span class="tc-no">❌ 아니오</span></td>
            <td><span class="tc-cond">🔶 조건부</span></td>
            <td>테이블 헤더, 섹션 타이틀</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>

  <!-- ─── 핵심 정리 ─── -->
  <div class="summary-grid">
    <div class="sum-card sc-static">
      <div class="sc-dot"></div>
      <div class="sc-name">static</div>
      <div class="sc-desc">기본값. 흐름대로 배치. top/left 무효.</div>
      <div class="sc-when">✦ 특별한 배치 불필요할 때</div>
    </div>
    <div class="sum-card sc-relative">
      <div class="sc-dot"></div>
      <div class="sc-name">relative</div>
      <div class="sc-desc">자기 자신 기준 이동. 원래 자리 유지.</div>
      <div class="sc-when">✦ absolute 자식의 기준점 만들 때</div>
    </div>
    <div class="sum-card sc-absolute">
      <div class="sc-dot"></div>
      <div class="sc-name">absolute</div>
      <div class="sc-desc">흐름 이탈. 부모 기준 자유 배치.</div>
      <div class="sc-when">✦ 툴팁·뱃지·오버레이</div>
    </div>
    <div class="sum-card sc-fixed">
      <div class="sc-dot"></div>
      <div class="sc-name">fixed</div>
      <div class="sc-desc">뷰포트 고정. 스크롤 무관.</div>
      <div class="sc-when">✦ 내비바·플로팅 버튼</div>
    </div>
    <div class="sum-card sc-sticky">
      <div class="sc-dot"></div>
      <div class="sc-name">sticky</div>
      <div class="sc-desc">흐름 유지 + 임계점 도달 시 고정.</div>
      <div class="sc-when">✦ 테이블 헤더·섹션 타이틀</div>
    </div>
  </div>

</div>

<script>
  // absolute 데모 이동
  const absPositions = {
    tr:     { top:'20px',  right:'20px',  bottom:'auto', left:'auto' },
    tl:     { top:'20px',  right:'auto',  bottom:'auto', left:'20px' },
    br:     { top:'auto',  right:'20px',  bottom:'14px', left:'auto' },
    center: { top:'50%',   right:'auto',  bottom:'auto', left:'50%',  transform:'translate(-50%,-50%)' }
  };

  function moveAbs(pos) {
    const box = document.getElementById('abs-box');
    const p = absPositions[pos];
    box.style.top    = p.top;
    box.style.right  = p.right;
    box.style.bottom = p.bottom;
    box.style.left   = p.left;
    box.style.transform = p.transform || '';

    document.querySelectorAll('.abs-ctrl-btn').forEach(b => b.classList.remove('active'));
    document.getElementById('abs-btn-' + pos).classList.add('active');

    // 라벨 업데이트
    const labels = {
      tr:     'top: 20px · right: 20px',
      tl:     'top: 20px · left: 20px',
      br:     'bottom: 14px · right: 20px',
      center: 'top: 50% · left: 50% · transform: translate(-50%,-50%)'
    };
    box.textContent = 'absolute';
  }
</script>
</body>
</html>

결과와 배운 점

파워포인트로 만들어진 자료보다 훨씬 학생들이 이해를 잘 하는 것을 느낄 수 있었습니다.

강의 자료실에 올려놓고 보도록 했더니 홈페이지 활성화 효과도 있어서 좋습니다.

이번 기수에 참여해서 정말 유용한 꿀팁을 많이 얻고 있는 느낌입니다.

밀어주고 끌어주는

온·오프라인 AI 스터디

AI로 어디까지 할 수 있는지
직접 확인하실 분만 신청하세요.