CSS translate · rotate · scale 설명하는 웹 페이지 만들기

소개

학생들이 transform을 잘 이해를 못 하는 경우가 많아서 자료를 만들어 보았습니다.

자료를 만들어서 보여주다보니 질문이 좀 많아지는 느낌이 있습니다.

질문이 많아진다는 것은 좋은 거겠죠?

진행 방법

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

claude를 사용했습니다.

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 Transform 완전 이해</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: #0f1117;
      --surface: #181c27;
      --surface2: #1e2334;
      --border: #2a2f45;
      --text: #e4e8f5;
      --muted: #6b7399;

      --translate-c: #60a5fa;
      --translate-bg: rgba(96,165,250,0.08);
      --translate-bd: rgba(96,165,250,0.28);

      --rotate-c: #f472b6;
      --rotate-bg: rgba(244,114,182,0.08);
      --rotate-bd: rgba(244,114,182,0.28);

      --scale-c: #34d399;
      --scale-bg: rgba(52,211,153,0.08);
      --scale-bd: rgba(52,211,153,0.28);

      --skew-c: #fbbf24;
      --skew-bg: rgba(251,191,36,0.08);
      --skew-bd: rgba(251,191,36,0.28);

      --center-c: #a78bfa;
      --center-bg: rgba(167,139,250,0.08);
      --center-bd: rgba(167,139,250,0.28);

      --code-bg: #0d0f14;
      --shadow: 0 2px 16px rgba(0,0,0,0.3);
    }

    * { 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;
    }

    body::before {
      content: '';
      position: fixed; inset: 0;
      background:
        radial-gradient(ellipse at 15% 20%, rgba(96,165,250,0.06) 0%, transparent 50%),
        radial-gradient(ellipse at 85% 70%, rgba(244,114,182,0.05) 0%, transparent 50%);
      pointer-events: none; z-index: 0;
    }

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

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

    .hero h1 {
      font-size: clamp(2.2rem,5vw,3.5rem);
      font-weight: 900; letter-spacing: -.03em;
      line-height: 1.1; margin-bottom: 16px;
    }

    .hero h1 .t1 { color: var(--translate-c); }
    .hero h1 .t2 { color: var(--rotate-c); }
    .hero h1 .t3 { color: var(--scale-c); }

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

    /* 히어로 애니메이션 박스 */
    .hero-demo {
      display: flex; justify-content: center;
      gap: 24px; flex-wrap: wrap;
      margin-top: 8px;
    }

    .hd-box {
      width: 64px; height: 64px;
      border-radius: 10px;
      display: flex; align-items: center; justify-content: center;
      font-family: 'JetBrains Mono', monospace;
      font-size: 10px; font-weight: 700;
      letter-spacing: .05em;
    }

    .hd-translate {
      background: var(--translate-bg);
      border: 2px solid var(--translate-bd);
      color: var(--translate-c);
      animation: heroTranslate 2.5s ease-in-out infinite;
    }

    .hd-rotate {
      background: var(--rotate-bg);
      border: 2px solid var(--rotate-bd);
      color: var(--rotate-c);
      animation: heroRotate 2.5s linear infinite;
    }

    .hd-scale {
      background: var(--scale-bg);
      border: 2px solid var(--scale-bd);
      color: var(--scale-c);
      animation: heroScale 2.5s ease-in-out infinite;
    }

    .hd-skew {
      background: var(--skew-bg);
      border: 2px solid var(--skew-bd);
      color: var(--skew-c);
      animation: heroSkew 2.5s ease-in-out infinite;
    }

    @keyframes heroTranslate { 0%,100%{transform:translateY(0)}50%{transform:translateY(-16px)} }
    @keyframes heroRotate    { from{transform:rotate(0deg)}to{transform:rotate(360deg)} }
    @keyframes heroScale     { 0%,100%{transform:scale(1)}50%{transform:scale(1.3)} }
    @keyframes heroSkew      { 0%,100%{transform:skewX(0deg)}50%{transform:skewX(15deg)} }

    .quick-nav {
      display: flex; justify-content: center;
      gap: 8px; flex-wrap: wrap; margin-top: 24px;
    }

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

    .qn-tr { border-color: var(--translate-bd); color: var(--translate-c); }
    .qn-ro { border-color: var(--rotate-bd);    color: var(--rotate-c); }
    .qn-sc { border-color: var(--scale-bd);     color: var(--scale-c); }
    .qn-sk { border-color: var(--skew-bd);      color: var(--skew-c); }
    .qn-cn { border-color: var(--center-bd);    color: var(--center-c); }
    .qn-btn:hover { filter: brightness(1.4); transform: translateY(-2px); }

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

    /* ── 섹션 ── */
    .section { margin-top: 64px; animation: fadeUp .5s ease both; }
    .section:nth-child(1){animation-delay:.05s}
    .section:nth-child(2){animation-delay:.10s}
    .section:nth-child(3){animation-delay:.15s}
    .section:nth-child(4){animation-delay:.20s}
    .section:nth-child(5){animation-delay:.25s}
    .section:nth-child(6){animation-delay:.30s}
    .section:nth-child(7){animation-delay:.35s}

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

    .badge {
      font-family: 'JetBrains Mono', monospace;
      font-size: 12px; font-weight: 700;
      padding: 5px 16px; border-radius: 8px;
      border: 1.5px solid; white-space: nowrap;
    }

    .badge-tr { background:var(--translate-bg); border-color:var(--translate-bd); color:var(--translate-c); }
    .badge-ro { background:var(--rotate-bg);    border-color:var(--rotate-bd);    color:var(--rotate-c); }
    .badge-sc { background:var(--scale-bg);     border-color:var(--scale-bd);     color:var(--scale-c); }
    .badge-sk { background:var(--skew-bg);      border-color:var(--skew-bd);      color:var(--skew-c); }
    .badge-cn { background:var(--center-bg);    border-color:var(--center-bd);    color:var(--center-c); }
    .badge-quiz{ background:rgba(251,191,36,0.08); border-color:rgba(251,191,36,0.3); color:var(--skew-c); }

    .sh-title h2 { font-size: 1.35rem; font-weight: 800; letter-spacing: -.02em; margin-bottom: 2px; }
    .sh-title p  { font-size: 13px; color: var(--muted); }

    /* ── 설명 카드 ── */
    .desc-card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 14px;
      padding: 20px 26px;
      margin-bottom: 16px;
      font-size: 14.5px; line-height: 1.95;
      color: #b0b8d8; box-shadow: var(--shadow);
    }
    .desc-card strong { color: var(--text); }

    .key-point {
      display: inline-flex; align-items: center;
      gap: 8px; margin-top: 12px;
      padding: 9px 15px; border-radius: 8px;
      font-size: 13px; font-weight: 600;
    }
    .kp-tr { background:var(--translate-bg); border:1px solid var(--translate-bd); color:var(--translate-c); }
    .kp-ro { background:var(--rotate-bg);    border:1px solid var(--rotate-bd);    color:var(--rotate-c); }
    .kp-sc { background:var(--scale-bg);     border:1px solid var(--scale-bd);     color:var(--scale-c); }
    .kp-sk { background:var(--skew-bg);      border:1px solid var(--skew-bd);      color:var(--skew-c); }
    .kp-cn { background:var(--center-bg);    border:1px solid var(--center-bd);    color:var(--center-c); }

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

    .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; }

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

    .kw  { color: #cba6f7; }
    .pr  { color: #89b4fa; }
    .vl  { color: #a6e3a1; }
    .cm  { color: #45475a; font-style: italic; }
    .sel { color: #89dceb; }
    .hl-tr { color: var(--translate-c); font-weight:700; }
    .hl-ro { color: var(--rotate-c);    font-weight:700; }
    .hl-sc { color: var(--scale-c);     font-weight:700; }
    .hl-sk { color: var(--skew-c);      font-weight:700; }
    .hl-cn { color: var(--center-c);    font-weight:700; }

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

    .demo-top {
      padding: 12px 18px;
      background: var(--surface2);
      border-bottom: 1px solid var(--border);
      display: flex; align-items: center;
      gap: 12px; flex-wrap: wrap;
    }

    .demo-top span {
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px; color: var(--muted);
      letter-spacing: .12em; text-transform: uppercase;
    }

    .ctrl-group { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

    .ctrl-label {
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px; color: var(--muted);
    }

    input[type=range] {
      accent-color: var(--translate-c);
      cursor: pointer; width: 120px;
    }

    .val-badge {
      font-family: 'JetBrains Mono', monospace;
      font-size: 12px; color: var(--translate-c);
      background: var(--translate-bg);
      border: 1px solid var(--translate-bd);
      border-radius: 6px; padding: 2px 10px;
      min-width: 60px; text-align: center;
    }

    .demo-stage {
      height: 200px;
      display: flex; align-items: center; justify-content: center;
      position: relative;
      background: repeating-linear-gradient(
        0deg, transparent, transparent 29px,
        rgba(255,255,255,0.03) 29px, rgba(255,255,255,0.03) 30px
      ),
      repeating-linear-gradient(
        90deg, transparent, transparent 29px,
        rgba(255,255,255,0.03) 29px, rgba(255,255,255,0.03) 30px
      );
    }

    /* 십자 중앙 표시 */
    .stage-center {
      position: absolute;
      top: 50%; left: 50%;
      transform: translate(-50%,-50%);
      width: 20px; height: 20px;
    }

    .stage-center::before, .stage-center::after {
      content: '';
      position: absolute;
      background: rgba(255,255,255,0.1);
    }

    .stage-center::before { width:1px;height:100%;left:50%;transform:translateX(-50%); }
    .stage-center::after  { height:1px;width:100%;top:50%;transform:translateY(-50%); }

    .demo-box {
      width: 80px; height: 80px;
      border-radius: 10px;
      display: flex; align-items: center; justify-content: center;
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px; font-weight: 700;
      transition: transform .25s ease;
      position: relative;
    }

    .demo-code-live {
      padding: 12px 18px;
      background: var(--code-bg);
      border-top: 1px solid var(--border);
      font-family: 'JetBrains Mono', monospace;
      font-size: 12.5px; color: var(--muted);
    }

    /* ── 가운데 정렬 시각화 ── */
    .center-visual {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 14px; overflow: hidden;
      box-shadow: var(--shadow);
      margin-bottom: 16px;
    }

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

    .cv-stage {
      height: 200px;
      position: relative;
      background: #0d0f14;
    }

    .cv-parent {
      position: absolute;
      top: 50%; left: 50%;
      transform: translate(-50%,-50%);
      width: 340px; height: 140px;
      border: 2px dashed rgba(167,139,250,0.3);
      border-radius: 8px;
    }

    .cv-parent-label {
      position: absolute;
      top: 6px; left: 10px;
      font-family: 'JetBrains Mono', monospace;
      font-size: 9px; color: rgba(167,139,250,0.5);
      letter-spacing: .1em;
    }

    /* top:0, left:0 기준점 표시 */
    .cv-dot-tl {
      position: absolute;
      top: 0; left: 0;
      width: 10px; height: 10px;
      border-radius: 50%;
      background: var(--rotate-c);
      transform: translate(-50%, -50%);
    }

    .cv-dot-label {
      position: absolute;
      font-family: 'JetBrains Mono', monospace;
      font-size: 9px; white-space: nowrap;
    }

    /* 화살표 */
    .cv-arrow-h, .cv-arrow-v {
      position: absolute;
      background: rgba(96,165,250,0.4);
    }

    .cv-arrow-h { height: 1px; top: 50%; left: 0; }
    .cv-arrow-v { width: 1px; left: 50%; top: 0; }

    .cv-child {
      position: absolute;
      top: 50%; left: 50%;
      transform: translate(-50%,-50%);
      width: 100px; height: 50px;
      background: var(--center-bg);
      border: 2px solid var(--center-bd);
      border-radius: 6px;
      display: flex; align-items: center; justify-content: center;
      font-family: 'JetBrains Mono', monospace;
      font-size: 11px; color: var(--center-c);
      font-weight: 700;
    }

    /* 단계별 설명 */
    .step-list {
      display: flex; flex-direction: column; gap: 10px;
      padding: 18px;
    }

    .step-item {
      display: flex; align-items: flex-start; gap: 14px;
    }

    .step-num {
      width: 28px; height: 28px;
      border-radius: 50%;
      display: flex; align-items: center; justify-content: center;
      font-family: 'JetBrains Mono', monospace;
      font-size: 12px; font-weight: 700;
      flex-shrink: 0;
    }

    .sn-1 { background: var(--rotate-bg);  border:1px solid var(--rotate-bd);  color:var(--rotate-c); }
    .sn-2 { background: var(--rotate-bg);  border:1px solid var(--rotate-bd);  color:var(--rotate-c); }
    .sn-3 { background: var(--center-bg);  border:1px solid var(--center-bd);  color:var(--center-c); }

    .step-content { flex: 1; }
    .step-title { font-size: 14px; font-weight: 700; margin-bottom: 3px; }
    .step-desc  { font-size: 13px; color: var(--muted); line-height: 1.6; }

    code {
      font-family: 'JetBrains Mono', monospace;
      background: rgba(255,255,255,0.07);
      padding: 1px 7px; border-radius: 4px;
      font-size: 12.5px; color: var(--center-c);
    }

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

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

    .cmp-table td {
      padding: 13px 16px;
      border-bottom: 1px solid rgba(255,255,255,0.04);
      color: #9099b8; vertical-align: middle;
    }

    .cmp-table tr:last-child td { border-bottom: none; }
    .cmp-table tr:hover td { background: var(--surface2); }
    .cmp-table td:first-child { font-family:'JetBrains Mono',monospace; font-size:13px; font-weight:700; }
    .cmp-table .row-tr td:first-child { color: var(--translate-c); }
    .cmp-table .row-ro td:first-child { color: var(--rotate-c); }
    .cmp-table .row-sc td:first-child { color: var(--scale-c); }
    .cmp-table .row-sk td:first-child { color: var(--skew-c); }

    /* ── 퀴즈 ── */
    .quiz-inner { padding: 24px 28px; }

    .quiz-scenario {
      background: var(--surface2);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 18px 20px;
      margin-bottom: 20px;
    }

    .qs-label {
      font-family: 'JetBrains Mono', monospace;
      font-size: 10px; letter-spacing: .15em;
      text-transform: uppercase; color: var(--muted);
      margin-bottom: 10px;
    }

    .quiz-q {
      font-size: 15px; font-weight: 700;
      line-height: 1.8; margin-bottom: 6px;
    }

    .quiz-q code { color: var(--center-c); }

    .quiz-sub {
      font-size: 13px; color: var(--muted); line-height: 1.7;
    }

    .quiz-opts { display: flex; flex-direction: column; gap: 10px; }

    .quiz-opt {
      display: flex; align-items: flex-start;
      gap: 13px; padding: 14px 18px;
      background: var(--surface2);
      border: 1.5px solid var(--border);
      border-radius: 12px; cursor: pointer;
      transition: all .2s; text-align: left;
      width: 100%; color: var(--text);
      font-family: 'Noto Sans KR', sans-serif;
    }

    .quiz-opt:hover:not(:disabled) {
      border-color: var(--center-c);
      background: var(--center-bg);
    }

    .opt-num {
      width: 28px; height: 28px; border-radius: 50%;
      border: 1.5px solid var(--border);
      display: flex; align-items: center; justify-content: center;
      font-family: 'JetBrains Mono', monospace;
      font-size: 12px; font-weight: 700;
      flex-shrink: 0; margin-top: 1px;
      transition: all .2s;
      background: var(--surface);
    }

    .opt-code {
      font-family: 'JetBrains Mono', monospace;
      font-size: 13px; color: #89b4fa;
      line-height: 1.8; margin-bottom: 3px;
    }

    .opt-desc { font-size: 12px; color: var(--muted); }

    .quiz-opt.correct {
      border-color: var(--scale-c);
      background: var(--scale-bg);
    }

    .quiz-opt.correct .opt-num {
      background: var(--scale-c);
      border-color: var(--scale-c);
      color: #000;
    }

    .quiz-opt.wrong {
      border-color: var(--rotate-c);
      background: var(--rotate-bg);
      opacity: .65;
    }

    .quiz-opt.wrong .opt-num {
      background: var(--rotate-c);
      border-color: var(--rotate-c);
      color: #fff;
    }

    .quiz-opt:disabled { cursor: not-allowed; }

    .quiz-feedback {
      display: none;
      margin-top: 18px; padding: 16px 20px;
      border-radius: 12px;
      font-size: 14px; line-height: 1.85;
    }

    .quiz-feedback.show { display: block; }
    .quiz-feedback.ok   { background:var(--scale-bg);  border:1px solid var(--scale-bd);  color:var(--scale-c); }
    .quiz-feedback.fail { background:var(--rotate-bg); border:1px solid var(--rotate-bd); color:var(--rotate-c); }

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

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

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

    .sc-dot { width:10px;height:10px;border-radius:50%;margin-bottom:12px; }
    .sc-name { font-family:'JetBrains Mono',monospace; font-size:14px; font-weight:700; margin-bottom:8px; }
    .sc-desc { font-size:13px; color:var(--muted); line-height:1.7; }
    .sc-code { font-family:'JetBrains Mono',monospace; font-size:11.5px; background:var(--surface2); border:1px solid var(--border); border-radius:6px; padding:5px 10px; margin-top:8px; }

    @keyframes fadeUp { from{opacity:0;transform:translateY(20px);}to{opacity:1;transform:translateY(0);} }
  </style>
</head>
<body>

<!-- 히어로 -->
<div class="hero">
  <div class="hero-tag">CSS · Transform · 완전 이해</div>
  <h1>
    <span class="t1">translate</span> ·
    <span class="t2">rotate</span> ·
    <span class="t3">scale</span>
  </h1>
  <p class="hero-sub">
    요소를 이동·회전·확대·기울이는 CSS transform!<br/>
    가운데 정렬의 핵심 비밀까지 완전히 이해해봐요.
  </p>
  <div class="hero-demo">
    <div class="hd-box hd-translate">move</div>
    <div class="hd-box hd-rotate">rotate</div>
    <div class="hd-box hd-scale">scale</div>
    <div class="hd-box hd-skew">skew</div>
  </div>
  <div class="quick-nav">
    <a href="#sec-tr" class="qn-btn qn-tr">translate</a>
    <a href="#sec-ro" class="qn-btn qn-ro">rotate</a>
    <a href="#sec-sc" class="qn-btn qn-sc">scale</a>
    <a href="#sec-sk" class="qn-btn qn-sk">skew</a>
    <a href="#sec-cn" class="qn-btn qn-cn">가운데 정렬</a>
  </div>
</div>

<div class="wrapper">

  <!-- ── 01 translate ── -->
  <div class="section" id="sec-tr">
    <div class="sec-header">
      <div class="badge badge-tr">translate</div>
      <div class="sh-title">
        <h2>이동시키기</h2>
        <p>X(좌우) · Y(상하) 방향으로 요소를 이동</p>
      </div>
    </div>

    <div class="desc-card">
      <strong>translate(x, y)</strong>는 요소를 <strong>X축(좌우)</strong><strong>Y축(상하)</strong>으로 이동시켜요.<br/>
      <strong>position</strong>의 top/left와 달리, 원래 자리를 유지하면서 이동해요.<br/>
      양수 → 오른쪽·아래로 / 음수 → 왼쪽·위로 이동해요.
      <br/>
      <div class="key-point kp-tr">📌 translateX(x) · translateY(y) · translate(x, y) 모두 사용 가능!</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">/* X축으로 50px 이동 */</span>
<span class="sel">.box</span> { <span class="pr">transform</span>: <span class="hl-tr">translateX(50px)</span>; }

<span class="cm">/* Y축으로 -30px 이동 (위로) */</span>
<span class="sel">.box</span> { <span class="pr">transform</span>: <span class="hl-tr">translateY(-30px)</span>; }

<span class="cm">/* X, Y 동시 이동 */</span>
<span class="sel">.box</span> { <span class="pr">transform</span>: <span class="hl-tr">translate(50px, -30px)</span>; }

<span class="cm">/* % 단위 사용 — 자기 자신 크기 기준! */</span>
<span class="sel">.box</span> { <span class="pr">transform</span>: <span class="hl-tr">translate(-50%, -50%)</span>; }</pre>
    </div>

    <!-- 인터랙티브 데모 -->
    <div class="demo-wrap">
      <div class="demo-top">
        <span>실습</span>
        <div class="ctrl-group">
          <div class="ctrl-label">X:</div>
          <input type="range" id="tr-x" min="-80" max="80" value="0" oninput="updateTranslate()"/>
          <div class="val-badge" id="tr-x-val">0px</div>
        </div>
        <div class="ctrl-group">
          <div class="ctrl-label">Y:</div>
          <input type="range" id="tr-y" min="-60" max="60" value="0" oninput="updateTranslate()"/>
          <div class="val-badge" id="tr-y-val">0px</div>
        </div>
      </div>
      <div class="demo-stage">
        <div class="stage-center"></div>
        <div class="demo-box" id="tr-box"
          style="background:var(--translate-bg);border:2px solid var(--translate-bd);color:var(--translate-c);">
          box
        </div>
      </div>
      <div class="demo-code-live" id="tr-code">transform: translate(0px, 0px);</div>
    </div>
  </div>

  <!-- ── 02 rotate ── -->
  <div class="section" id="sec-ro">
    <div class="sec-header">
      <div class="badge badge-ro">rotate</div>
      <div class="sh-title">
        <h2>회전시키기</h2>
        <p>요소를 시계 방향(+) 또는 반시계 방향(-)으로 회전</p>
      </div>
    </div>

    <div class="desc-card">
      <strong>rotate(각도)</strong>는 요소를 중심점을 기준으로 <strong>회전</strong>시켜요.<br/>
      단위는 <strong>deg(도)</strong>를 사용해요. 양수 → 시계 방향 / 음수 → 반시계 방향이에요.<br/>
      기본 중심점은 요소의 <strong>정중앙</strong>이에요. (transform-origin으로 변경 가능)
      <br/>
      <div class="key-point kp-ro">📌 rotate(45deg) = 45도 시계방향 · rotate(-90deg) = 반시계방향</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">/* 45도 시계 방향 회전 */</span>
<span class="sel">.box</span> { <span class="pr">transform</span>: <span class="hl-ro">rotate(45deg)</span>; }

<span class="cm">/* 90도 반시계 방향 */</span>
<span class="sel">.box</span> { <span class="pr">transform</span>: <span class="hl-ro">rotate(-90deg)</span>; }

<span class="cm">/* 애니메이션과 조합 */</span>
<span class="sel">.spinner</span> {
  <span class="pr">animation</span>: <span class="vl">spin 1s linear infinite</span>;
}
<span class="kw">@keyframes</span> spin {
  <span class="kw">to</span> { <span class="pr">transform</span>: <span class="hl-ro">rotate(360deg)</span>; }
}</pre>
    </div>

    <div class="demo-wrap">
      <div class="demo-top">
        <span>실습</span>
        <div class="ctrl-group">
          <div class="ctrl-label">각도:</div>
          <input type="range" id="ro-deg" min="-180" max="180" value="0" oninput="updateRotate()" style="accent-color:var(--rotate-c);"/>
          <div class="val-badge" id="ro-val" style="color:var(--rotate-c);background:var(--rotate-bg);border-color:var(--rotate-bd);">0deg</div>
        </div>
      </div>
      <div class="demo-stage">
        <div class="demo-box" id="ro-box"
          style="background:var(--rotate-bg);border:2px solid var(--rotate-bd);color:var(--rotate-c);">
          box
        </div>
      </div>
      <div class="demo-code-live" id="ro-code">transform: rotate(0deg);</div>
    </div>
  </div>

  <!-- ── 03 scale ── -->
  <div class="section" id="sec-sc">
    <div class="sec-header">
      <div class="badge badge-sc">scale</div>
      <div class="sh-title">
        <h2>확대·축소하기</h2>
        <p>요소의 크기를 비율로 조절</p>
      </div>
    </div>

    <div class="desc-card">
      <strong>scale(배율)</strong>는 요소를 <strong>확대하거나 축소</strong>해요.<br/>
      1 = 원본 크기 / 2 = 2배 확대 / 0.5 = 절반 축소 / 음수 = 뒤집기!<br/>
      실제 레이아웃 공간은 변하지 않고, <strong>시각적으로만 크기가 변해요.</strong>
      <br/>
      <div class="key-point kp-sc">📌 scaleX(x) · scaleY(y) · scale(x, y) 모두 사용 가능!</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">/* 2배 확대 */</span>
<span class="sel">.box</span> { <span class="pr">transform</span>: <span class="hl-sc">scale(2)</span>; }

<span class="cm">/* 가로만 1.5배 */</span>
<span class="sel">.box</span> { <span class="pr">transform</span>: <span class="hl-sc">scaleX(1.5)</span>; }

<span class="cm">/* 호버 시 살짝 커지는 효과 */</span>
<span class="sel">.btn:hover</span> {
  <span class="pr">transform</span>: <span class="hl-sc">scale(1.05)</span>;
  <span class="pr">transition</span>: <span class="vl">transform 0.2s</span>;
}

<span class="cm">/* -1 = 좌우 뒤집기 */</span>
<span class="sel">.flip</span> { <span class="pr">transform</span>: <span class="hl-sc">scaleX(-1)</span>; }</pre>
    </div>

    <div class="demo-wrap">
      <div class="demo-top">
        <span>실습</span>
        <div class="ctrl-group">
          <div class="ctrl-label">배율:</div>
          <input type="range" id="sc-val" min="0.1" max="2.5" step="0.1" value="1" oninput="updateScale()" style="accent-color:var(--scale-c);"/>
          <div class="val-badge" id="sc-display" style="color:var(--scale-c);background:var(--scale-bg);border-color:var(--scale-bd);">1.0</div>
        </div>
      </div>
      <div class="demo-stage">
        <div class="demo-box" id="sc-box"
          style="background:var(--scale-bg);border:2px solid var(--scale-bd);color:var(--scale-c);">
          box
        </div>
      </div>
      <div class="demo-code-live" id="sc-code">transform: scale(1.0);</div>
    </div>
  </div>

  <!-- ── 04 skew ── -->
  <div class="section" id="sec-sk">
    <div class="sec-header">
      <div class="badge badge-sk">skew</div>
      <div class="sh-title">
        <h2>기울이기</h2>
        <p>요소를 X축 또는 Y축으로 비스듬히 기울임</p>
      </div>
    </div>

    <div class="desc-card">
      <strong>skew(각도)</strong>는 요소를 <strong>비스듬히 기울여요.</strong><br/>
      평행사변형처럼 찌그러지는 효과를 낼 수 있어요.<br/>
      단위는 <strong>deg(도)</strong>를 사용해요.
      <br/>
      <div class="key-point kp-sk">📌 skewX(x) · skewY(y) · skew(x, y) — 디자인 강조 효과에 활용!</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">/* X축으로 20도 기울이기 */</span>
<span class="sel">.box</span> { <span class="pr">transform</span>: <span class="hl-sk">skewX(20deg)</span>; }

<span class="cm">/* Y축으로 기울이기 */</span>
<span class="sel">.box</span> { <span class="pr">transform</span>: <span class="hl-sk">skewY(10deg)</span>; }

<span class="cm">/* 배너·버튼 강조 효과 */</span>
<span class="sel">.banner</span> { <span class="pr">transform</span>: <span class="hl-sk">skewX(-15deg)</span>; }</pre>
    </div>

    <div class="demo-wrap">
      <div class="demo-top">
        <span>실습</span>
        <div class="ctrl-group">
          <div class="ctrl-label">X:</div>
          <input type="range" id="sk-x" min="-40" max="40" value="0" oninput="updateSkew()" style="accent-color:var(--skew-c);"/>
          <div class="val-badge" id="sk-x-val" style="color:var(--skew-c);background:var(--skew-bg);border-color:var(--skew-bd);">0deg</div>
        </div>
        <div class="ctrl-group">
          <div class="ctrl-label">Y:</div>
          <input type="range" id="sk-y" min="-40" max="40" value="0" oninput="updateSkew()" style="accent-color:var(--skew-c);"/>
          <div class="val-badge" id="sk-y-val" style="color:var(--skew-c);background:var(--skew-bg);border-color:var(--skew-bd);">0deg</div>
        </div>
      </div>
      <div class="demo-stage">
        <div class="demo-box" id="sk-box"
          style="background:var(--skew-bg);border:2px solid var(--skew-bd);color:var(--skew-c);">
          box
        </div>
      </div>
      <div class="demo-code-live" id="sk-code">transform: skew(0deg, 0deg);</div>
    </div>
  </div>

  <!-- ── 05 가운데 정렬 ── -->
  <div class="section" id="sec-cn">
    <div class="sec-header">
      <div class="badge badge-cn">가운데 정렬</div>
      <div class="sh-title">
        <h2>translate(-50%, -50%) 의 비밀</h2>
        <p>absolute + top:50% + left:50% + translate(-50%,-50%)</p>
      </div>
    </div>

    <div class="desc-card">
      <code>top: 50%</code><code>left: 50%</code><strong>부모의 중앙</strong>으로 이동시키지만,<br/>
      기준점이 요소의 <strong>왼쪽 상단 모서리</strong>라서 완벽한 중앙이 아니에요.<br/>
      <code>translate(-50%, -50%)</code><strong>자기 자신의 절반만큼 위·왼쪽으로 당겨</strong>서 정중앙에 맞춰요!
      <br/>
      <div class="key-point kp-cn">📌 % 단위는 자기 자신의 크기 기준 — 요소 크기 몰라도 항상 정중앙!</div>
    </div>

    <!-- 시각적 설명 -->
    <div class="center-visual">
      <div class="cv-header">🎯 단계별 시각화</div>
      <div class="cv-stage">
        <div class="cv-parent">
          <div class="cv-parent-label">PARENT (position: relative)</div>
          <div class="cv-arrow-h" style="width:50%;"></div>
          <div class="cv-arrow-v" style="height:50%;"></div>
          <div class="cv-dot-tl">
            <div class="cv-dot-label" style="top:12px;left:4px;color:var(--rotate-c);font-size:8px;">기준점</div>
          </div>
          <div class="cv-child">CHILD</div>
        </div>
      </div>
      <div class="step-list">
        <div class="step-item">
          <div class="step-num sn-1">1</div>
          <div class="step-content">
            <div class="step-title"><code>position: absolute</code> + <code>top: 50%</code> + <code>left: 50%</code></div>
            <div class="step-desc">요소의 왼쪽 상단 모서리가 부모 정중앙으로 이동해요. 아직 완전 중앙이 아니에요!</div>
          </div>
        </div>
        <div class="step-item">
          <div class="step-num sn-2">2</div>
          <div class="step-content">
            <div class="step-title"><code>translate(-50%, -50%)</code></div>
            <div class="step-desc">자기 자신 너비의 50%만큼 왼쪽으로, 높이의 50%만큼 위로 당겨요. 이제 정중앙!</div>
          </div>
        </div>
        <div class="step-item">
          <div class="step-num sn-3"></div>
          <div class="step-content">
            <div class="step-title">완벽한 가운데 정렬 완성!</div>
            <div class="step-desc">요소 크기가 달라져도 항상 부모의 정중앙에 배치돼요.</div>
          </div>
        </div>
      </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">.parent</span> {
  <span class="pr">position</span>: <span class="vl">relative</span>;  <span class="cm">/* 부모는 relative */</span>
}

<span class="sel">.child</span> {
  <span class="pr">position</span>: <span class="vl">absolute</span>;

  <span class="pr">top</span>:  <span class="vl">50%</span>;        <span class="cm">/* 부모 높이의 50% 아래로 */</span>
  <span class="pr">left</span>: <span class="vl">50%</span>;        <span class="cm">/* 부모 너비의 50% 오른쪽으로 */</span>

  <span class="pr">transform</span>: <span class="hl-cn">translate(-50%, -50%)</span>;
  <span class="cm">/*  ↑ 자기 너비의 50% 왼쪽으로  */</span>
  <span class="cm">/*    자기 높이의 50% 위쪽으로   */</span>
}</pre>
    </div>

    <!-- 비교표 -->
    <div style="overflow-x:auto;">
      <table class="cmp-table">
        <thead>
          <tr>
            <th>함수</th><th>설명</th><th>단위</th><th>주요 활용</th>
          </tr>
        </thead>
        <tbody>
          <tr class="row-tr"><td>translate(x,y)</td><td>X·Y 방향 이동</td><td>px, %, em</td><td>이동 효과, 가운데 정렬</td></tr>
          <tr class="row-ro"><td>rotate(deg)</td><td>중심점 기준 회전</td><td>deg, turn</td><td>스피너, 화살표 방향</td></tr>
          <tr class="row-sc"><td>scale(n)</td><td>크기 확대·축소</td><td>숫자(배율)</td><td>호버 효과, 줌</td></tr>
          <tr class="row-sk"><td>skew(x,y)</td><td>X·Y축 기울이기</td><td>deg</td><td>배너, 디자인 강조</td></tr>
        </tbody>
      </table>
    </div>
  </div>

  <!-- ── 퀴즈 ── -->
  <div class="section">
    <div class="sec-header">
      <div class="badge badge-quiz">QUIZ</div>
      <div class="sh-title">
        <h2>확인 퀴즈</h2>
        <p>배운 내용을 확인해봐요!</p>
      </div>
    </div>

    <div class="demo-wrap">
      <div class="quiz-inner">
        <div class="quiz-scenario">
          <div class="qs-label">📋 상황</div>
          <p style="font-size:14px;color:var(--muted);line-height:1.8;margin-bottom:12px;">
            다음 코드에서 요소를 부모의 정중앙에 배치하려고 해요:
          </p>
          <div class="code-block" style="margin:0;">
            <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 style="padding:14px 18px;font-size:13px;"><span class="sel">.child</span> {
  <span class="pr">position</span>: <span class="vl">absolute</span>;
  <span class="pr">top</span>:  <span class="vl">50%</span>;
  <span class="pr">left</span>: <span class="vl">50%</span>;
  <span class="pr">transform</span>: <span class="hl-cn">???</span>;
}</pre>
          </div>
        </div>

        <div class="quiz-q">
          <code>???</code> 에 들어갈 올바른 값은 무엇인가요?
        </div>
        <p class="quiz-sub" style="margin:8px 0 18px;">힌트: % 단위는 자기 자신의 크기 기준이에요!</p>

        <div class="quiz-opts">

          <button class="quiz-opt" onclick="checkQ(this, false)">
            <div class="opt-num">1</div>
            <div>
              <div class="opt-code">translate(50%, 50%)</div>
              <div class="opt-desc">양수 방향으로 이동</div>
            </div>
          </button>

          <button class="quiz-opt" onclick="checkQ(this, true)">
            <div class="opt-num">2</div>
            <div>
              <div class="opt-code">translate(-50%, -50%)</div>
              <div class="opt-desc">음수 방향으로 자신의 절반만큼 당기기</div>
            </div>
          </button>

          <button class="quiz-opt" onclick="checkQ(this, false)">
            <div class="opt-num">3</div>
            <div>
              <div class="opt-code">translate(-50px, -50px)</div>
              <div class="opt-desc">px 단위로 고정 이동</div>
            </div>
          </button>

          <button class="quiz-opt" onclick="checkQ(this, false)">
            <div class="opt-num">4</div>
            <div>
              <div class="opt-code">translate(0, 0)</div>
              <div class="opt-desc">이동 없음</div>
            </div>
          </button>

        </div>

        <div class="quiz-feedback" id="quiz-fb"></div>
      </div>
    </div>
  </div>

  <!-- 핵심 정리 -->
  <div class="summary-grid">
    <div class="sum-card">
      <div class="sc-dot" style="background:var(--translate-c);"></div>
      <div class="sc-name" style="color:var(--translate-c);">translate</div>
      <div class="sc-desc">X·Y 방향으로 이동. % 단위는 자기 크기 기준.</div>
      <div class="sc-code" style="color:var(--translate-c);">translate(x, y)</div>
    </div>
    <div class="sum-card">
      <div class="sc-dot" style="background:var(--rotate-c);"></div>
      <div class="sc-name" style="color:var(--rotate-c);">rotate</div>
      <div class="sc-desc">중심점 기준 회전. deg 단위 사용.</div>
      <div class="sc-code" style="color:var(--rotate-c);">rotate(45deg)</div>
    </div>
    <div class="sum-card">
      <div class="sc-dot" style="background:var(--scale-c);"></div>
      <div class="sc-name" style="color:var(--scale-c);">scale</div>
      <div class="sc-desc">배율로 확대·축소. 레이아웃 공간 유지.</div>
      <div class="sc-code" style="color:var(--scale-c);">scale(1.5)</div>
    </div>
    <div class="sum-card">
      <div class="sc-dot" style="background:var(--skew-c);"></div>
      <div class="sc-name" style="color:var(--skew-c);">skew</div>
      <div class="sc-desc">비스듬히 기울이기. 디자인 강조 효과.</div>
      <div class="sc-code" style="color:var(--skew-c);">skewX(15deg)</div>
    </div>
    <div class="sum-card">
      <div class="sc-dot" style="background:var(--center-c);"></div>
      <div class="sc-name" style="color:var(--center-c);">가운데 정렬</div>
      <div class="sc-desc">top:50% + left:50% + translate(-50%,-50%)</div>
      <div class="sc-code" style="color:var(--center-c);">translate(-50%,-50%)</div>
    </div>
  </div>

</div>

<script>
  // translate 데모
  function updateTranslate() {
    const x = document.getElementById('tr-x').value;
    const y = document.getElementById('tr-y').value;
    document.getElementById('tr-x-val').textContent = x + 'px';
    document.getElementById('tr-y-val').textContent = y + 'px';
    document.getElementById('tr-box').style.transform = `translate(${x}px, ${y}px)`;
    document.getElementById('tr-code').textContent = `transform: translate(${x}px, ${y}px);`;
  }

  // rotate 데모
  function updateRotate() {
    const deg = document.getElementById('ro-deg').value;
    document.getElementById('ro-val').textContent = deg + 'deg';
    document.getElementById('ro-box').style.transform = `rotate(${deg}deg)`;
    document.getElementById('ro-code').textContent = `transform: rotate(${deg}deg);`;
  }

  // scale 데모
  function updateScale() {
    const v = parseFloat(document.getElementById('sc-val').value).toFixed(1);
    document.getElementById('sc-display').textContent = v;
    document.getElementById('sc-box').style.transform = `scale(${v})`;
    document.getElementById('sc-code').textContent = `transform: scale(${v});`;
  }

  // skew 데모
  function updateSkew() {
    const x = document.getElementById('sk-x').value;
    const y = document.getElementById('sk-y').value;
    document.getElementById('sk-x-val').textContent = x + 'deg';
    document.getElementById('sk-y-val').textContent = y + 'deg';
    document.getElementById('sk-box').style.transform = `skew(${x}deg, ${y}deg)`;
    document.getElementById('sk-code').textContent = `transform: skew(${x}deg, ${y}deg);`;
  }

  // 퀴즈
  let answered = false;

  function checkQ(btn, isCorrect) {
    if (answered) return;
    answered = true;

    document.querySelectorAll('.quiz-opt').forEach(o => {
      o.disabled = true;
      o.style.pointerEvents = 'none';
    });

    btn.classList.add(isCorrect ? 'correct' : 'wrong');
    if (!isCorrect) document.querySelectorAll('.quiz-opt')[1].classList.add('correct');

    const fb = document.getElementById('quiz-fb');
    fb.classList.add('show', isCorrect ? 'ok' : 'fail');

    if (isCorrect) {
      fb.innerHTML = `✅ <strong>정답!</strong><br/>
        <code>translate(-50%, -50%)</code>가 정답이에요.<br/>
        top:50% + left:50%로 기준점이 부모 중앙에 왔지만, 요소의 왼쪽 상단이 기준이라서 아직 완전 중앙이 아니에요.<br/>
        <code>translate(-50%, -50%)</code>로 자기 자신의 절반만큼 위·왼쪽으로 당겨야 정확히 중앙이 돼요!`;
    } else {
      fb.innerHTML = `❌ <strong>틀렸어요!</strong> 정답은 <strong>2번</strong>이에요.<br/>
        • <strong>1번</strong>: +50%는 더 오른쪽·아래로 밀려버려요!<br/>
        • <strong>3번</strong>: px 단위는 요소 크기가 달라지면 맞지 않아요. % 단위가 항상 정확해요!<br/>
        • <strong>4번</strong>: translate(0,0)은 이동이 없어서 기준점이 그대로예요.`;
    }
  }
</script>
</body>
</html>

결과와 배운 점

자료를 만들다 보니까 평소에 그냥 넘어가던 것들도 이건 왜 그런 거지? 라는 생각을 자꾸 하게 되더라구요.

그것이 자료의 아이디어가 되는 느낌이 듭니다.

또 현대적인 코드라는 것이 무엇인가, 공부를 더 해야겠다는 깨달음도 있었습니다.

화이팅

뉴스레터 무료 구독