GSAP 사례 연구
Animate auto height
카드 · MIT (local companion); GSAP Standard No Charge License (runtime)
갤러리 안에서는 화면을 벗어나거나 움직임을 멈추면 예제도 닫힙니다. 다시 재생할 때는 처음부터 시작해요.
저장한 HTML에서는 갤러리의 재생 설정이 적용되지 않아요.
SOURCE FILES
독립 예제 코드 읽기
사례의 구도나 문서의 원리를 바탕으로 새로 만든 예제입니다. 아래 코드를 복사해 직접 응용할 수 있습니다. 실행 HTML에는 미리보기를 위한 실행 환경이 들어 있습니다.
vendor/gsap-previews/sources/gsap-6ec2aaf96f9c/study-brief.json실행 안내·자료
{
"id": "gsap-6ec2aaf96f9c",
"sourceId": "gsap",
"title": "Animate auto height",
"sourceUrl": "https://demos.gsap.com/demo/animate-auto-height",
"demoUrl": "https://codepen.io/GreenSock/pen/QwjNwpo",
"sourceStatus": "unresolved",
"sourceReason": "Public page unavailable through web browsing",
"sourceRevision": null,
"sourceLicense": null,
"originalDemoAcquired": false,
"disposition": "ready_for_independent_study",
"evidenceBasis": "topic_only_catalog_title_plus_independently_verified_official_api",
"studyId": "gsap-6ec2aaf96f9c",
"sharedStudyCanonicalId": null,
"label": "Independently authored documentation study; original Pen unavailable",
"catalogEvidence": {
"localPath": "artifacts/full-import/gsap/remaining-docs/catalog-evidence.json",
"sha256": "4f2a4f9aa64456b85b905b562b60d7733629b41026787b3057cba29e8b10de69",
"bytes": 30117,
"recordId": "gsap-6ec2aaf96f9c"
},
"sourceInputs": [
{
"kind": "authored-study-specification",
"localPath": "artifacts/full-import/gsap/remaining-docs/study-specs.json",
"sha256": "cbb89331eda8783b5cf2f173b326b9195eeabfb223374fb60019161d9f46d4a6",
"bytes": 52279,
"recordId": "gsap-6ec2aaf96f9c"
},
{
"kind": "official-catalog-metadata",
"localPath": "artifacts/full-import/gsap/remaining-docs/catalog-evidence.json",
"sha256": "4f2a4f9aa64456b85b905b562b60d7733629b41026787b3057cba29e8b10de69",
"bytes": 30117,
"recordId": "gsap-6ec2aaf96f9c"
},
{
"kind": "official-documentation-evidence",
"localPath": "artifacts/full-import/gsap/remaining-docs/official-documentation-evidence.json",
"sha256": "bfa25f561e61c597569cf1bc0b3538e6dce4b9985a932775ceab41e665ca24a2",
"bytes": 11430,
"recordIds": [
"css",
"tween"
]
},
{
"kind": "original-library-source",
"localPath": "artifacts/full-import/gsap/library/src/CSSPlugin.js",
"sha256": "8e141f02d26f00f0d3259aa480e3eddff55ad25e0363272321b883602af1f824",
"gitBlob": "5f459a1fd9cbb34dfffa659e1175c431e7e1e62f",
"revision": "13e2b790546426a1a2e0e9b409f3f8dc6d6611f2",
"sourcePath": "src/CSSPlugin.js",
"ranges": [
{
"id": "css-auto-height",
"startLine": 282,
"endLine": 299,
"rangeSha256": "6976cf434584cf7d3f110865c9bb9531bde44e3d50280a062e58e6e2db5dc9a1"
},
{
"id": "css-auto-endpoint",
"startLine": 320,
"endLine": 331,
"rangeSha256": "7258a65b4723cc0802229b223945a42c0803a98c31159a493083f650983cbbcd"
}
]
},
{
"kind": "original-library-source",
"localPath": "artifacts/full-import/gsap/library/src/gsap-core.js",
"sha256": "8063816e0e7c3e6c07970fb06feafb06146fd266db5d8346419176b3d496e284",
"gitBlob": "c673b9e2e0ee04832afb13cb892ae75f503e268c",
"revision": "13e2b790546426a1a2e0e9b409f3f8dc6d6611f2",
"sourcePath": "src/gsap-core.js",
"ranges": [
{
"id": "overwrite-all",
"startLine": 2403,
"endLine": 2411,
"rangeSha256": "3033b90f012d6d87330a8fca1b6ca3006c831d80930d99e3a8a16d360a32bba7"
},
{
"id": "tween-factories",
"startLine": 2637,
"endLine": 2657,
"rangeSha256": "23ae9d51865c9b7966bbc3b6cdbdb560fc790ff19e3b8bb524a3493fbf60c58a"
}
]
}
],
"studiedPrinciple": "Animate a content wrapper between zero and its intrinsic height using CSSPlugin's auto endpoint measurement. The text-expansion button and its interaction contract are newly authored.",
"namedApis": [
{
"name": "gsap.to(wrapper, {height: 'auto'})",
"purpose": "Measure the intrinsic endpoint and interpolate from the current computed height.",
"libraryEvidence": [
"css-auto-height",
"css-auto-endpoint",
"tween-factories"
],
"documentationEvidence": [
"css"
]
},
{
"name": "gsap.to(wrapper, {height: 0, overwrite: true})",
"purpose": "Collapse from the current value and replace the previous height tween.",
"libraryEvidence": [
"css-auto-height",
"overwrite-all"
],
"documentationEvidence": [
"tween"
]
}
],
"implementationBrief": {
"scene": "One native button followed by a clipped wrapper containing independently written, noninteractive explanatory text. Put padding on an inner element so height zero does not retain outer padding.",
"controls": [
{
"label": "Expand / Collapse",
"type": "button",
"action": "In animated mode toggle desiredOpen, update aria-expanded, and animate toward auto or zero. In static/reduced-motion mode disable this button, keep desiredOpen and aria-expanded true, and label the visible state Expanded."
},
{
"label": "Short / Long text",
"type": "select",
"action": "Replace the inner paragraph with an authored short or long variant. If open, settle height to auto after cancelling the old tween so the changed content is readable."
},
{
"label": "Static view",
"type": "toggle",
"action": "Show all text with natural height, stop motion, set desiredOpen and aria-expanded true, and disable the collapse button. Leaving static mode keeps the paragraph open and re-enables collapse; it does not collapse automatically."
}
],
"mechanismSteps": [
"Leave text visible in base HTML/CSS. Only initialize a closed height of zero after JavaScript has loaded and animated mode is enabled.",
"Use a desiredOpen boolean and one owned tween reference. Each button click replaces the old tween, then uses height:'auto' or height:0 with overwrite:true.",
"On successful opening completion explicitly retain height:auto. Guard completion with the current desired state so an interrupted tween cannot overwrite a newer request.",
"Keep the inner content in layout while measuring. Do not apply display:none to it before computing the opening endpoint.",
"If width or content changes during an opening transition, cancel the old measured transition and settle to the requested endpoint. This resize policy is an authored choice.",
"In static mode or reduced motion expose the entire paragraph with no fixed height, desiredOpen=true, and aria-expanded=true. Disable the Expand/Collapse button while content is permanently exposed. Base no-JavaScript markup uses the same expanded, disabled state.",
"Leaving static mode keeps the open natural-height state and re-enables the Collapse action. Only the initial non-reduced-motion JavaScript initialization uses the authored collapsed starting state."
]
},
"authoredDefaults": {
"durationSeconds": 0.4,
"ease": "power2.inOut",
"initialAnimatedState": "collapsed",
"shortTextLength": "one new sentence",
"longTextLength": "three new paragraphs",
"assets": "system font and original text only"
},
"verificationRequired": [
"At open completion the wrapper has natural height and all text is visible; at closed completion its content box height is zero.",
"Rapid expand-collapse-expand ends in the last requested state without an old completion callback changing the height.",
"Switch long text and resize from 1440px to 320px while opening; no lasting clipped text remains after settling.",
"Keyboard activation updates aria-expanded. Static, reduced-motion, and no-JavaScript states expose text with aria-expanded=true and the collapse button disabled; returning to animated mode keeps content open until the next action."
],
"unsupportedClaims": [
"Original composition, assets, timing, breakpoint behavior, and live interaction are not observable in captured frames. This brief does not establish visual fidelity or recover original code.",
"The catalog has an empty description, body, tags, and API links. Its title selects the study topic only; no original accordion, text content, trigger, layout, duration, or reverse behavior was observed.",
"An auto endpoint is measured for a tween; this is not a guarantee of continuously tracking content changes during that tween."
],
"dependencies": {
"package": "gsap",
"version": "3.15.0",
"revision": "13e2b790546426a1a2e0e9b409f3f8dc6d6611f2",
"repository": "https://github.com/greensock/GSAP",
"plugins": [],
"registration": "Core includes CSSPlugin; no separate plugin registration required.",
"runtimeCandidates": [
{
"path": "dist/gsap.js",
"localPath": "artifacts/full-import/gsap/library/dist/gsap.js",
"gitBlob": "ec5ffee3f099bc454f2f1c79cee6a55ba97e7400",
"sha256": "01effe0e121c22ba0f885a3f091f3dc6d1f0d445c4fba37dcb298e314eca5cfb",
"bytes": 179557
}
],
"license": {
"name": "GSAP Standard 'No Charge' License (custom, not MIT)",
"localPath": "artifacts/full-import/gsap/licenses/gsap-standard-license.html",
"sha256": "bf8536e67b77db780f7db0c14a645f184f554bb19e938d1e52d58986e2f10528",
"bytes": 6988,
"url": "https://gsap.com/community/standard-license/"
},
"originalDependencyVersionKnown": false
},
"runtimeVerified": false,
"implementationWritten": false
}
vendor/gsap-previews/sources/gsap-6ec2aaf96f9c/source-inputs.json실행 안내·자료
[
{
"kind": "authored-study-specification",
"localPath": "artifacts/full-import/gsap/remaining-docs/study-specs.json",
"sha256": "cbb89331eda8783b5cf2f173b326b9195eeabfb223374fb60019161d9f46d4a6",
"bytes": 52279,
"recordId": "gsap-6ec2aaf96f9c"
},
{
"kind": "official-catalog-metadata",
"localPath": "artifacts/full-import/gsap/remaining-docs/catalog-evidence.json",
"sha256": "4f2a4f9aa64456b85b905b562b60d7733629b41026787b3057cba29e8b10de69",
"bytes": 30117,
"recordId": "gsap-6ec2aaf96f9c"
},
{
"kind": "official-documentation-evidence",
"localPath": "artifacts/full-import/gsap/remaining-docs/official-documentation-evidence.json",
"sha256": "bfa25f561e61c597569cf1bc0b3538e6dce4b9985a932775ceab41e665ca24a2",
"bytes": 11430,
"recordIds": [
"css",
"tween"
]
},
{
"kind": "original-library-source",
"localPath": "artifacts/full-import/gsap/library/src/CSSPlugin.js",
"sha256": "8e141f02d26f00f0d3259aa480e3eddff55ad25e0363272321b883602af1f824",
"gitBlob": "5f459a1fd9cbb34dfffa659e1175c431e7e1e62f",
"revision": "13e2b790546426a1a2e0e9b409f3f8dc6d6611f2",
"sourcePath": "src/CSSPlugin.js",
"ranges": [
{
"id": "css-auto-height",
"startLine": 282,
"endLine": 299,
"rangeSha256": "6976cf434584cf7d3f110865c9bb9531bde44e3d50280a062e58e6e2db5dc9a1"
},
{
"id": "css-auto-endpoint",
"startLine": 320,
"endLine": 331,
"rangeSha256": "7258a65b4723cc0802229b223945a42c0803a98c31159a493083f650983cbbcd"
}
]
},
{
"kind": "original-library-source",
"localPath": "artifacts/full-import/gsap/library/src/gsap-core.js",
"sha256": "8063816e0e7c3e6c07970fb06feafb06146fd266db5d8346419176b3d496e284",
"gitBlob": "c673b9e2e0ee04832afb13cb892ae75f503e268c",
"revision": "13e2b790546426a1a2e0e9b409f3f8dc6d6611f2",
"sourcePath": "src/gsap-core.js",
"ranges": [
{
"id": "overwrite-all",
"startLine": 2403,
"endLine": 2411,
"rangeSha256": "3033b90f012d6d87330a8fca1b6ca3006c831d80930d99e3a8a16d360a32bba7"
},
{
"id": "tween-factories",
"startLine": 2637,
"endLine": 2657,
"rangeSha256": "23ae9d51865c9b7966bbc3b6cdbdb560fc790ff19e3b8bb524a3493fbf60c58a"
}
]
}
]
함께 쓰는 파일 4개 보기
vendor/gsap-previews/sources/gsap-6ec2aaf96f9c/index.html실행 안내·자료
<main class="study"><p class="study-label">공식 API 기반 · 독립 예제</p><header><h2>내용이 정하는 높이</h2><p>고정된 끝점 대신 실제 글의 높이로 열립니다. 처음에는 내용을 모두 보여 줍니다.</p></header><div class="height-card"><div class="height-caption">FIELD NOTES <span>01 / OBSERVATION</span></div><div id="height-wrapper"><div class="height-inner" id="height-content"><p>움직임은 구조를 이해하도록 돕습니다. 내용이 길어지면 상자는 정해 둔 숫자 대신 그 내용의 자연 높이에 맞춥니다.</p></div></div></div><div class="controls"><button id="toggle" type="button" disabled aria-expanded="true" aria-controls="height-wrapper">전체 내용 표시</button><label>글 길이<select id="length"><option selected>짧게</option><option>길게</option></select></label><label><input id="static-mode" type="checkbox">정적 보기</label></div><p class="readout" id="reading" aria-live="off"></p></main>vendor/gsap-previews/sources/gsap-6ec2aaf96f9c/style.css실행 안내·자료
*{box-sizing:border-box}body{background:#111522;color:#f5f2eb;font-family:system-ui,sans-serif;margin:0}button{font:inherit}.study{margin:0 auto;max-width:1100px;min-height:100vh;padding:clamp(20px,4vw,42px)}.study-label{color:#95efd2;font-size:12px;letter-spacing:.08em;margin:0 0 24px}header h2{font-size:clamp(24px,4vw,40px);letter-spacing:-.04em;margin:0 0 12px}header p{color:#aeb7c9;line-height:1.6;margin:0 0 24px;max-width:62ch}.controls{display:flex;flex-wrap:wrap;gap:8px;margin:20px 0}button{background:#26314b;border:1px solid #617394;border-radius:9px;color:#f5f2eb;cursor:pointer;padding:9px 14px}button:hover{background:#334261}button:focus-visible{outline:3px solid #95efd2;outline-offset:3px}button:disabled{cursor:default;opacity:.45}.stage{align-items:center;background:radial-gradient(ellipse at top,#243253,#141c2d);border:1px solid #3d4c68;border-radius:20px;display:flex;justify-content:center;min-height:220px;overflow:hidden;position:relative}.note{color:#aeb7c9;font-size:13px;line-height:1.5}.readout{color:#95efd2;font-family:ui-monospace,monospace;font-size:13px;min-height:2em}.stage svg{display:block;max-width:100%}code{font-family:ui-monospace,monospace}button[aria-pressed="true"]{background:#95efd2;color:#111522}
.study{padding:18px;max-width:960px}.study-label{margin-bottom:10px}header h2{font-size:clamp(22px,4vw,32px);margin-bottom:8px}header p{font-size:14px;margin-bottom:16px}.controls{align-items:center;gap:9px;margin:14px 0}.controls label{align-items:center;color:#cbd4e3;display:flex;flex-wrap:wrap;font-size:13px;gap:7px}.controls input[type=range]{accent-color:#95efd2;max-width:145px}.controls select{background:#26314b;border:1px solid #617394;border-radius:7px;color:#f5f2eb;font:inherit;padding:6px}.stage{min-height:180px}.readout{line-height:1.5;margin:8px 0}.small-code{background:#172036;border:1px solid #354258;border-radius:10px;color:#c3d1e7;font-size:12px;line-height:1.5;max-height:180px;overflow:auto;padding:12px;white-space:pre-wrap;word-break:break-word}.pair{display:flex;gap:12px;justify-content:center}.pair figure{flex:1;margin:0;max-width:300px;min-width:0;padding:8px}.pair figcaption{color:#aeb7c9;font-family:ui-monospace,monospace;font-size:12px;margin-bottom:6px;text-align:center}.pair svg{display:block;max-height:210px;width:100%}.tile-art{align-items:center;border-radius:14px;color:#152039;display:flex;justify-content:center}.tile-art svg{height:65%;width:65%}.menu-stage{min-height:196px;padding:18px}.menu-panels{display:flex;gap:10px;max-width:570px;width:100%}.menu-panel{align-items:center;background:linear-gradient(135deg,#95efd2,#75d8ed);border-radius:14px;color:#142137;display:flex;flex:1;flex-direction:column;font-weight:650;gap:10px;justify-content:center;min-height:135px;min-width:0;padding:12px}.menu-panel:nth-child(2){background:linear-gradient(135deg,#fbadce,#897dff)}.menu-panel:nth-child(3){background:linear-gradient(135deg,#ffd28c,#fc7f8e)}.menu-panel svg{height:58px;width:58px}.icon-bars{display:inline-flex;flex-direction:column;gap:5px;margin-right:7px;vertical-align:middle}.icon-bars span{background:currentColor;display:block;height:2px;width:17px}.endpoint-note{color:#aeb7c9;font-size:12px;line-height:1.5}button[hidden],[hidden]{display:none!important}@media(max-width:380px){.study{padding:14px}.controls label{max-width:100%}.menu-panels{gap:6px}.menu-panel{font-size:12px;padding:7px}.pair{gap:0}}
.height-card{background:linear-gradient(135deg,#95efd2,#75d8ed);border-radius:18px;color:#142137;overflow:hidden}.height-caption{align-items:center;border-bottom:1px solid #14213733;display:flex;flex-wrap:wrap;font-size:13px;font-weight:750;gap:12px;justify-content:space-between;padding:20px}.height-caption span{font-size:10px;letter-spacing:.08em}.height-inner{padding:20px}.height-inner p{line-height:1.7;margin:0 0 12px}.height-inner p:last-child{margin:0}#height-wrapper{overflow:hidden}vendor/gsap-previews/sources/gsap-6ec2aaf96f9c/script.js실행 안내·자료
gsap.registerPlugin();
const q=selector=>document.querySelector(selector),qa=selector=>Array.from(document.querySelectorAll(selector));
const studyAbort=new AbortController(),studyCleanup=[];
const listen=(target,type,handler)=>target.addEventListener(type,handler,{signal:studyAbort.signal});
const preference=matchMedia('(prefers-reduced-motion: reduce)'),staticControl=q('#static-mode');
staticControl.checked=preference.matches;staticControl.disabled=preference.matches;
const isStatic=()=>staticControl.checked||preference.matches;
let applyMode=()=>{};
listen(staticControl,'change',()=>applyMode());
listen(preference,'change',()=>{staticControl.checked=preference.matches;staticControl.disabled=preference.matches;applyMode();});
listen(window,'pagehide',()=>{studyCleanup.forEach(cleanup=>cleanup());studyAbort.abort();});
const reading=text=>q('#reading').textContent=text;
const wrapper=q('#height-wrapper'),content=q('#height-content'),toggle=q('#toggle');
const shortText='<p>움직임은 구조를 이해하도록 돕습니다. 내용이 길어지면 상자는 정해 둔 숫자 대신 그 내용의 자연 높이에 맞춥니다.</p>';
const longText=shortText+'<p>새 입력이 오면 진행 중인 높이 전환을 정리하고 마지막 요청을 따릅니다. 이전 열기의 완료가 최신 닫힘을 덮어쓰지 않도록 상태를 함께 확인합니다.</p><p>화면 폭이 달라져 줄이 다시 나뉘면 측정한 끝점도 달라집니다. 이 연구는 그 순간 전환을 끝내고 현재 요청에 맞는 자연 높이 또는 닫힌 높이로 정착합니다.</p>';
let desiredOpen=true,heightTween=null,generation=0;
function reflect(){toggle.disabled=isStatic();toggle.textContent=isStatic()?'전체 내용 표시':desiredOpen?'닫기':'열기';toggle.setAttribute('aria-expanded',String(isStatic()||desiredOpen));toggle.setAttribute('aria-controls','height-wrapper');wrapper.setAttribute('aria-hidden',String(!isStatic()&&!desiredOpen));reading(isStatic()?'정적 보기 · 전체 내용 표시':desiredOpen?'열림 · 자연 높이로 정착':'닫힘 · 높이 0');}
function settle(){if(isStatic())desiredOpen=true;generation++;heightTween?.kill();gsap.set(wrapper,{height:desiredOpen?'auto':0});reflect();}
function changeOpen(){desiredOpen=!desiredOpen;const token=++generation;heightTween?.kill();reflect();if(isStatic()){settle();return;}
heightTween=gsap.to(wrapper,{height:desiredOpen?'auto':0,duration:.4,ease:'power2.inOut',overwrite:true,onComplete:()=>{if(token===generation&&desiredOpen)gsap.set(wrapper,{height:'auto'});}});}
listen(toggle,'click',changeOpen);listen(q('#length'),'change',e=>{content.innerHTML=e.target.value==='길게'?longText:shortText;settle();});
let previousWidth=wrapper.clientWidth;const heightObserver=new ResizeObserver(()=>{const width=wrapper.clientWidth;if(width!==previousWidth){previousWidth=width;settle();}});heightObserver.observe(wrapper);
applyMode=settle;studyCleanup.push(()=>{heightTween?.kill();heightObserver.disconnect();});settle();LICENSE실행 안내·자료
Locally authored companion HTML/CSS/control wiring: MIT License.
Copyright (c) 2026 StyleGallery contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The typed source-inputs.json and study-brief.json preserve official catalog, documentation and original library source pointers. This is independently authored API research, not a recovered CodePen source or documentation snippet. The local MIT notice does not assign new licenses to referenced third-party sources.
GSAP 3.15.0: Copyright 2026, GreenSock. All rights reserved. Subject to https://gsap.com/standard-license. Original library notices are preserved.
BEHIND THE EXAMPLE
이 장면을 만드는 원리
자연 높이와 최신 열림 요청
최신 논리 상태와 완료 콜백의 유효성을 함께 검사하여 이전 전환이 새 상태를 덮지 않게 합니다.
- 이 예제에서는
- 공식 CSS 문서와 CSSPlugin 입력을 근거로 새 글 상자를 만들었습니다. changeOpen은 이전 높이 트윈을 정리하고 height:auto 또는 0으로 향하며 세대 번호가 일치하는 열기 완료에만 auto를 복원합니다. 글 변경과 폭 변경은 최신 요청의 끝 상태로 정착합니다. 정적 보기와 JavaScript 실행 전에는 전체 내용을 읽을 수 있으며 처음 열림은 독립 예제의 표시 선택입니다.
코드와 함께 확인하기
코드에서 찾기
function changeOpenscript.js마지막 요청, 트윈 제거, 세대 확인을 한 제어 경로에 둡니다.
function settlescript.js폭·내용·정적 모드 변경 시 표현을 현재 논리 상태에 맞춥니다.
직접 해보기
열기→닫기→열기를 0.4초 이내에 반복합니다.
살펴볼 변화최종 요청과 aria-expanded가 일치하고 과거 완료가 현재 높이를 덮지 않아야 합니다.
긴 글을 선택하고 전환 중 폭을 바꾸거나 정적 보기로 전환합니다.
살펴볼 변화자연 높이 또는 닫힘으로 정착하며 정적 모드에서는 전체 내용과 비활성 닫기 상태가 일치해야 합니다.
