Codrops 사례 연구

One state. Two surfaces.

섹션 · MIT

섹션 더 보기
CASE STUDY
One state. Two surfaces. 정적 미리보기

갤러리 안에서는 화면을 벗어나거나 움직임을 멈추면 예제도 닫힙니다. 다시 재생할 때는 처음부터 시작해요.

큰 화면으로 보기 새 탭실행 HTML 저장

저장한 HTML에서는 갤러리의 재생 설정이 적용되지 않아요.

SOURCE FILES

독립 예제 코드 읽기

7개 파일

사례의 구도나 문서의 원리를 바탕으로 새로 만든 예제입니다. 아래 코드를 복사해 직접 응용할 수 있습니다. 실행 HTML에는 미리보기를 위한 실행 환경이 들어 있습니다.

index.html독립 구현
파일 저장

<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data:; media-src data:; font-src data:; connect-src 'none'; base-uri 'none'; form-action 'none'"><title>One state. Two surfaces. · Independent article study</title><style>/* Generated by scripts/build-codrops-article-studies.mjs.
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.

Scope: independently authored study HTML, CSS, JavaScript, shaders and SVG only. Original articles, source projects, brands, artwork and screenshots are excluded. Their licenses and acquisition dispositions are unchanged.
*/

*{box-sizing:border-box}html{color-scheme:dark}html,body{margin:0;min-height:100%}body{background:var(--paper,#0c1321);color:var(--ink,#f4f3e9);font-family:Arial,Helvetica,sans-serif}button,input,select{font:inherit}button,select,input{min-width:0}button{background:transparent;border:1px solid #ffffff40;border-radius:999px;color:inherit;cursor:pointer;min-height:40px;padding:10px 16px}button:hover{background:#ffffff15}button[aria-pressed=true]{background:var(--accent,#dbff78);border-color:transparent;color:#122014}button:disabled{cursor:default;opacity:.6}button:focus-visible,input:focus-visible,select:focus-visible,summary:focus-visible,a:focus-visible{outline:3px solid var(--accent,#dbff78);outline-offset:4px}input,select{accent-color:var(--accent,#dbff78)}select,input[type=text],input[type=number]{background:#132235;border:1px solid #ffffff50;border-radius:7px;color:#fff;min-height:38px;padding:8px;width:100%}input[type=range]{display:block;min-height:30px;width:100%}a{color:inherit}svg{display:block;max-width:100%}h1,h2,p,figure{margin:0}h1{font-size:clamp(36px,6.5vw,78px);font-weight:500;letter-spacing:-.06em;line-height:.99}h2{font-size:24px;font-weight:500;letter-spacing:-.025em}p{line-height:1.55}.study{isolation:isolate;min-height:100vh;padding:clamp(18px,3.8vw,48px)}.masthead{align-items:center;display:flex;flex-wrap:wrap;font:10px/1.5 ui-monospace,monospace;gap:16px;justify-content:space-between;letter-spacing:.11em;margin-bottom:35px;text-transform:uppercase}.masthead strong{font-size:12px;letter-spacing:-.02em}.intro{display:grid;gap:18px;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);margin-bottom:30px;align-items:end}.intro p{color:var(--muted,#b7c7d4);font-size:13px;max-width:38em}.eyebrow{color:var(--accent,#dbff78);font:10px/1.4 ui-monospace,monospace;letter-spacing:.14em;margin-bottom:12px;text-transform:uppercase}.workbench{display:grid;gap:20px;grid-template-columns:minmax(0,1fr) 230px}.visual{background:var(--surface,#101d2b);border:1px solid #ffffff20;border-radius:20px;min-width:0;overflow:hidden;position:relative}.drawing{height:auto;width:100%}.art-canvas{aspect-ratio:1.55;display:block;height:auto;width:100%}.panel{align-content:start;display:grid;gap:17px}.control{display:grid;font-size:11px;gap:7px;min-width:0}.control>span{display:flex;gap:10px;justify-content:space-between}.control output{color:var(--accent,#dbff78);font-family:ui-monospace,monospace}.controls{display:flex;flex-wrap:wrap;gap:8px}.readout{background:#ffffff09;border:1px solid #ffffff18;border-radius:12px;font:11px/1.7 ui-monospace,monospace;overflow-wrap:anywhere;padding:14px;white-space:pre-line}.legend{align-items:center;display:flex;flex-wrap:wrap;font:10px/1.6 ui-monospace,monospace;gap:16px;padding:16px 20px}.legend i{background:var(--accent,#dbff78);border-radius:50%;display:inline-block;height:6px;margin-right:6px;width:6px}.contract{border-top:1px solid #ffffff20;color:var(--muted,#b7c7d4);font-size:11px;margin-top:28px;padding-top:18px}.contract summary{color:var(--ink,#f4f3e9);cursor:pointer;font-size:12px}.contract p{margin-top:10px;max-width:90ch}.motion-bar{align-items:center;border-top:1px solid #ffffff20;display:flex;flex-wrap:wrap;font-size:11px;gap:12px;justify-content:space-between;margin-top:26px;padding-top:18px}.motion-bar label{align-items:center;display:flex;gap:8px}.feedback{color:var(--muted,#b7c7d4);font:11px/1.5 ui-monospace,monospace;min-height:18px}.visually-hidden{clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;width:1px}[hidden]{display:none!important}.stage-note{background:#07111ac9;bottom:18px;color:#dfe8f0;font:11px/1.5 ui-monospace,monospace;left:18px;max-width:calc(100% - 36px);padding:8px 12px;position:absolute;z-index:2}.big-number{font-size:clamp(70px,14vw,180px);letter-spacing:-.08em;line-height:1}.plain-list{display:grid;gap:12px;list-style:none;margin:0;padding:0}.plain-list li{border-bottom:1px solid #ffffff20;padding:12px 0}.tabs{display:flex;flex-wrap:wrap;gap:7px}.tabs button{font-size:11px}.two{display:grid;gap:20px;grid-template-columns:1fr 1fr}.code-sample{color:#c9ecbe;font:11px/1.7 ui-monospace,monospace;overflow:auto;white-space:pre-wrap}.fallback{padding:30px}.fallback p{max-width:38ch}.static-gallery{display:none}.study[data-reduced=true] .static-gallery{display:grid}.study[data-reduced=true] .animated-story{display:none}.study[data-reduced=true] *{animation:none!important;transition:none!important}body[data-preview-status=error] .feedback{color:#ffbba3}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;scroll-behavior:auto!important;transition:none!important}}@media(max-width:700px){.intro{grid-template-columns:1fr}.workbench{grid-template-columns:1fr}.panel{grid-template-columns:repeat(2,minmax(0,1fr))}.panel>.readout,.panel>.controls{grid-column:1/-1}.masthead{margin-bottom:26px}.two{grid-template-columns:1fr}}@media(max-width:380px){.panel{gap:12px}.control{font-size:10px}.panel button{font-size:11px;padding:9px 12px}.legend{gap:8px;padding:12px}.visual{border-radius:13px}}


.cover-lab{--cover-accent:#e8ff69;background:#161426}.cover-painted{position:relative}.cover-canvas{aspect-ratio:1.38;display:block;height:auto;width:100%}.cover-surface-label{background:#191927cc;border:1px solid #ffffff30;border-radius:20px;color:#f2efde;font:8px/1.4 ui-monospace,monospace;left:14px;letter-spacing:.08em;padding:5px 8px;position:absolute;top:13px}.cover-dom{align-items:center;background:#f1efe4;border-top:5px solid var(--cover-accent);color:#292633;display:flex;gap:18px;justify-content:space-between;padding:20px 22px}.cover-dom>div{min-width:0}.cover-dom-label{color:#615e65;font:8px/1.4 ui-monospace,monospace;letter-spacing:.1em}.cover-dom h2{font-size:clamp(21px,2.8vw,32px);font-weight:700;letter-spacing:-.04em;line-height:1.1;margin-top:8px;overflow-wrap:anywhere;white-space:pre-wrap}.cover-dom-description{font:10px/1.5 ui-monospace,monospace;margin-top:8px}.cover-accent-swatch{background:var(--cover-accent);border:1px solid #29263333;border-radius:50%;box-shadow:inset -10px -5px 0 #29263314;flex:none;height:42px;width:42px}.cover-controls .control small{color:#adb0c1;font-size:9px;font-weight:400}.cover-help{color:#b7b8c7;font-size:11px}.cover-render-status{border-top:1px solid #ffffff22;color:#d9d9c8;font:9px/1.6 ui-monospace,monospace;padding-top:12px}.cover-failure{align-content:center;color:#f4e8d3;display:grid;gap:14px;min-height:240px;padding:32px}.cover-failure p{font-size:12px;max-width:40ch}.cover-failure strong{font-size:24px;font-weight:500}@media(max-width:700px){.cover-caption-input,.cover-controls>.cover-help,.cover-controls>.cover-render-status{grid-column:1/-1}.cover-canvas{aspect-ratio:1.1}.cover-dom{padding:17px}.cover-controls .controls{margin-top:0}}@media(max-height:550px) and (min-width:500px){.cover-workbench{gap:16px}.cover-canvas{aspect-ratio:auto;height:190px}.cover-dom{border-top-width:3px;gap:10px;padding:10px 14px}.cover-dom-label{font-size:6px}.cover-dom h2{font-size:19px;margin-top:4px}.cover-dom-description{font-size:8px;margin-top:4px}.cover-accent-swatch{height:28px;width:28px}.cover-surface-label{font-size:6px;left:10px;padding:4px 6px;top:9px}.cover-controls .control small{font-size:7px}.cover-controls .cover-help{font-size:9px;line-height:1.45}.cover-render-status{font-size:8px;padding-top:8px}}


@media(max-height:550px) and (min-width:500px){
.study{padding:18px}.masthead{font-size:8px;gap:10px;margin-bottom:16px}.masthead strong{font-size:10px}.intro{gap:18px;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);margin-bottom:18px}.intro h1{font-size:34px}.intro p{font-size:10px;line-height:1.5}.intro .eyebrow{font-size:8px;margin-bottom:8px}.workbench{gap:16px;grid-template-columns:minmax(0,1fr) 174px}.panel{gap:10px;grid-template-columns:1fr}.panel .control{font-size:9px;gap:3px}.panel .control input[type=range]{min-height:22px}.panel select,.panel input[type=text],.panel input[type=number]{font-size:10px;min-height:31px;padding:6px}.panel .controls{gap:5px}.panel button{font-size:9px;min-height:30px;padding:7px 10px}.panel .readout{font-size:8px;padding:10px}.art-canvas{aspect-ratio:auto!important;height:250px;object-fit:contain}.legend{font-size:8px;gap:8px;padding:10px 13px}.stage-note{bottom:12px;font-size:8px;left:12px;padding:5px 8px}.type-lab{min-height:280px;padding:23px}.type-heading{font-size:36px}.particle-rack{height:80px}.type-rule{font-size:7px}.bounce-lab>.drawing{height:230px;object-fit:contain}.track-graph{padding:9px 16px}.track-graph svg{height:43px}.member-lab{min-height:300px;padding:18px}.member-heading h2{font-size:24px}.member-collage{height:180px}.member-total .counter{font-size:52px}.member-total>span{font-size:7px}.member-card span{font-size:6px;padding:4px}.composer-surface{padding:20px}.composer-label{font-size:8px;margin-bottom:20px}.form-set{gap:10px}.form-set h2{font-size:20px;margin-top:12px}.form-set p{font-size:9px}.timeline-rows{padding:12px 17px}.mascot-lab>.drawing{height:250px;object-fit:contain}.pose-strip{padding:10px 14px}.pose-marker{font-size:8px;padding:6px 0}.fit-stage{min-height:270px;padding:20px}.fit-stage canvas{max-height:235px}.tree-columns{grid-template-columns:125px minmax(0,1fr);min-height:320px}.tree-inspector{padding:17px 12px}.tree-inspector pre{font-size:8px;margin-top:18px}.tree-rules{display:none}.tree-preview{min-height:0;padding:14px}.tree-width-label,.tree-caption{font-size:7px}.fixture-copy h2{font-size:1.6em}.fixture-copy p{font-size:.65em}.fixture-output{gap:10px;padding:12px}.fixture-art{max-height:90px}.semantic-lab{grid-template-columns:110px minmax(0,1fr);min-height:320px}.node-list{flex-direction:column;padding:12px 7px}.node-list button{font-size:8px;gap:5px;padding:9px 7px}.node-list button small{font-size:7px}.semantic-preview{padding:14px}.semantic-fixture{padding:8px}.semantic-fixture h2{font-size:27px}.semantic-fixture p{font-size:10px}.semantic-art{height:75px}.markup-panel{padding:15px}.markup-panel pre{font-size:8px}.plant-lab{padding:18px}.plant-heading h2{font-size:28px}.plant-heading span{font-size:8px}.plant-illustration{margin:-60px 0 -8px}.plant-illustration svg{height:250px}.plant-callouts{gap:5px}.plant-callouts button{font-size:7px;min-height:28px;padding:7px}.plant-feature,.care-plan{font-size:10px}.orbit-lab{min-height:300px}.orbit-camera{height:270px}.orbit-ring{height:170px;width:110px}.orbit-card{height:170px;width:110px}.orbit-foot{font-size:8px;padding:10px 14px}.botanical-window{aspect-ratio:1.5}.window-label h2{font-size:29px}.window-label span{font-size:8px}.gold-lab{padding:18px}.gold-heading h2{font-size:28px;margin-top:9px}.gold-heading>span,.gold-total>span{font-size:7px}.gold-lab>svg{height:175px;margin:-27px 0 -6px}.gold-total{padding-top:9px}.gold-total output{font-size:43px}.gold-disclaimer{font-size:9px}.story-controls{gap:10px;grid-template-columns:repeat(4,minmax(0,1fr));padding:10px 0;position:relative}.story-controls .control{font-size:8px}.story-controls input[type=range]{min-height:20px}.story-controls>.controls{grid-column:auto}.story-controls button{font-size:8px;min-height:27px;padding:6px 8px}.story-pin{top:10px}.story-pin>.visual{min-height:270px}.story-pin .art-canvas{height:270px}.story-copy{bottom:20px;left:20px;max-width:255px}.story-chapter h2{font-size:29px;margin:8px 0}.story-chapter p{font-size:10px}.story-chapter>span{font-size:8px}.moon-lab{aspect-ratio:auto;min-height:270px}.moon-progress,.cylinder-energy,.terrain-coordinates{font-size:8px}.story-skip{font-size:9px;margin-bottom:10px}.reading-chapter{grid-template-columns:1fr 1fr}.motion-bar{margin-top:18px}
}
</style></head><body><main id="root" class="study"><header class="masthead"><strong>STYLEGALLERY / STUDIES</strong><span>CODE + CURIOSITY / 22</span><span>INDEPENDENT EXAMPLE</span></header><div class="intro"><div><p class="eyebrow">22 / Semantic content &amp; pixel treatment</p><h1>One state. Two surfaces.</h1></div><p>Make a cover, then break it into pixels. The painted artwork and readable HTML share your caption and palette; the controls stay clear.</p></div><div class="workbench cover-workbench"><section class="visual cover-lab" aria-label="Two representations of your cover"><figure class="cover-painted"><canvas class="cover-canvas" data-cover-canvas aria-hidden="true"></canvas><div class="cover-failure" data-cover-failure hidden><strong>Illustration unavailable</strong><p data-cover-failure-detail>The semantic cover and controls remain available below.</p></div><figcaption class="cover-surface-label">01 / CANVAS · ORIGINAL DRAWING</figcaption></figure><article class="cover-dom" data-cover-dom aria-label="Semantic cover"><div><span class="cover-dom-label">02 / HTML · READABLE CONTENT</span><h2 data-cover-caption>AFTERGLOW</h2><p class="cover-dom-description" data-cover-description>Electric citrus · Default treatment</p></div><span class="cover-accent-swatch" data-cover-swatch aria-hidden="true"></span></article></section><aside class="panel cover-controls" aria-label="Shared cover controls"><label class="control cover-caption-input"><span>Caption <small>up to 28 characters</small></span><input type="text" data-control="caption" value="AFTERGLOW" maxlength="28" autocomplete="off" spellcheck="false"></label><label class="control"><span>Palette</span><select data-control="accent"><option value="citrus" selected>Electric citrus</option><option value="violet">Ultraviolet</option><option value="tidal">Tidal blue</option></select></label><label class="control"><span>Canvas treatment</span><select data-control="effect"><option value="Default" selected>Default</option><option value="Pixelated">Pixelated</option></select></label><div class="controls"><button type="button" data-action="reset-cover" >Reset cover</button></div><p class="cover-help">Edit once. Read both views.<br>Pixel treatment changes the drawing; your real text stays available.</p><output class="cover-render-status" data-cover-render-status aria-live="off">Preparing the drawing.</output></aside></div><div class="motion-bar"><label><input type="checkbox" data-static> Static view · less motion</label><p class="feedback" data-feedback aria-live="polite">Motion starts only when requested. All inputs are local.</p></div><details class="contract"><summary>What this study teaches</summary><p>One application state owns caption, accent and effect. Both a semantic HTML card and an independently drawn Canvas2D cover consume the same snapshot. Pixelated mode paints that cover into a smaller detached canvas, then enlarges it with image smoothing disabled. Inputs and resize events redraw the current state without replacing native controls. This is manual drawing of known content, not automatic HTML rasterization.</p><p>Independent implementation informed by “Exploring the HTML-in-Canvas Proposal”, Vittorio Retrivi, Codrops. This is newly authored code and artwork, not a recovered original project. The acquired original failed in the recorded environment because requestPaint was not callable; its success was not verified. This example does not implement the HTML-in-Canvas proposal, copy arbitrary HTML/CSS, transform DOM hit testing, or reproduce the original Fluid/Rain shaders. The duplicate canvas is hidden from the accessibility tree; visible semantic content and native controls remain outside it. No assistive-technology conformance is claimed. Caption input is bounded to 28 code units by the native field. Empty captions keep the artwork visible. Long captions shrink to fit the painted line and wrap in the semantic card. Both presentation modes are stationary. A Canvas2D failure keeps the DOM representation usable and emits an error instead of readiness.</p></details><noscript><div class="fallback"><p>This study’s explanation and initial HTML illustration remain available. Interactive rendering needs JavaScript.</p></div></noscript></main><script>/* Generated by scripts/build-codrops-article-studies.mjs.
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.

Scope: independently authored study HTML, CSS, JavaScript, shaders and SVG only. Original articles, source projects, brands, artwork and screenshots are excluded. Their licenses and acquisition dispositions are unchanged.
*/
(() => {
'use strict';
const id="codrops-9e12943621d5",stage=document.querySelector('.study');
const preview=window.__STYLEGALLERY_PREVIEW__={id,status:'loading',errors:[]};
const report=error=>{const message=error instanceof Error?error.message:String(error);if(!preview.errors.includes(message))preview.errors.push(message);preview.status='error';document.body.dataset.previewStatus='error';parent.postMessage({type:'sg-preview-error',id,message},'*');const feedback=document.querySelector('[data-feedback]');if(feedback)feedback.textContent=message;};
addEventListener('error',event=>report(event.error||event.message));addEventListener('unhandledrejection',event=>report(event.reason));
const $=selector=>stage.querySelector(selector),$$=selector=>[...stage.querySelectorAll(selector)];
const value=key=>$('[data-control="'+key+'"]').value,number=key=>Number(value(key));
const clamp=(x,a=0,b=1)=>Math.max(a,Math.min(b,x)),lerp=(a,b,t)=>a+(b-a)*t;
const setText=(selector,text)=>{const node=$(selector);if(node)node.textContent=text;};
const media=matchMedia('(prefers-reduced-motion: reduce)');
let isReduced=media.matches,playing=false,time=0,draw=()=>{},raf=0,last=0,disposed=false;
const cleanups=[],on=(target,type,fn,options)=>{target.addEventListener(type,fn,options);cleanups.push(()=>target.removeEventListener(type,fn,options));};
const listen=(key,fn)=>on($('[data-action="'+key+'"]'),'click',fn);
const repaint=()=>{if(!disposed){try{draw(0);}catch(error){report(error);}}};
const loop=now=>{raf=0;if(disposed||!playing||isReduced)return;const dt=Math.min((now-last)/1000,.05);last=now;time+=dt;try{draw(dt);}catch(error){report(error);stop();return;}raf=requestAnimationFrame(loop);};
function stop(){playing=false;cancelAnimationFrame(raf);raf=0;$$('[data-play]').forEach(button=>{button.setAttribute('aria-pressed','false');button.textContent=isReduced?'Static view active':'Play motion';});}
function start(){if(isReduced)return;playing=true;last=performance.now();$$('[data-play]').forEach(button=>{button.setAttribute('aria-pressed','true');button.textContent='Pause motion';});if(!raf)raf=requestAnimationFrame(loop);}
function applyMotionPreference(){isReduced=media.matches||$('[data-static]').checked;stage.dataset.reduced=String(isReduced);if(isReduced)stop();$$('[data-play]').forEach(button=>{button.disabled=isReduced;button.textContent=playing?'Pause motion':isReduced?'Static view active':'Play motion';});repaint();}
function install(render){draw=render;applyMotionPreference();}
const observe=()=>{const root=document.getElementById('root'),elements=[...root.querySelectorAll('*')],rect=root.getBoundingClientRect();parent.postMessage({type:'sg-preview-observation',id,diagnostics:{textLength:root.textContent.trim().length,elementCount:elements.length,visibleElementCount:elements.filter(element=>{const r=element.getBoundingClientRect(),s=getComputedStyle(element);return r.width>0&&r.height>0&&s.display!=='none'&&s.visibility!=='hidden'&&Number(s.opacity)!==0;}).length,images:[],canvases:$$('canvas').map(c=>({width:c.width,height:c.height})),rootRect:{width:rect.width,height:rect.height},documentWidth:document.documentElement.scrollWidth,viewportWidth:innerWidth}},'*');};
on(media,'change',applyMotionPreference);on($('[data-static]'),'change',applyMotionPreference);
$$('[data-play]').forEach(button=>on(button,'click',()=>playing?stop():start()));
$$('[data-control]').forEach(input=>on(input,'input',()=>{const output=$('[data-value="'+input.dataset.control+'"]');if(output)output.textContent=input.value;if(input.dataset.control!=='story')repaint();}));
on(document,'visibilitychange',()=>{if(document.hidden)stop();});
on(window,'pagehide',()=>{disposed=true;stop();cleanups.forEach(fn=>fn());});
function canvas2D(selector,width=900,height=580){const canvas=$(selector);canvas.width=width;canvas.height=height;const context=canvas.getContext('2d');if(!context)throw Error('A 2D drawing surface is unavailable.');return {canvas,ctx:context,width,height};}
function createGL(canvas,fragment,vertex){const gl=canvas.getContext('webgl',{alpha:false,antialias:true,preserveDrawingBuffer:true});if(!gl){canvas.hidden=true;const fallback=$('[data-gl-fallback]');if(fallback)fallback.hidden=false;throw Error('WebGL is unavailable. The study explanation and controls remain readable.');}
 const shader=(type,source)=>{const s=gl.createShader(type);gl.shaderSource(s,source);gl.compileShader(s);if(!gl.getShaderParameter(s,gl.COMPILE_STATUS))throw Error(gl.getShaderInfoLog(s));return s;};
 const vs=shader(gl.VERTEX_SHADER,vertex||'attribute vec2 aPosition;varying vec2 vUv;void main(){vUv=aPosition*.5+.5;gl_Position=vec4(aPosition,0.,1.);}'),fs=shader(gl.FRAGMENT_SHADER,fragment),program=gl.createProgram();gl.attachShader(program,vs);gl.attachShader(program,fs);gl.linkProgram(program);if(!gl.getProgramParameter(program,gl.LINK_STATUS))throw Error(gl.getProgramInfoLog(program));gl.useProgram(program);
 const buffer=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,buffer);if(!vertex){gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),gl.STATIC_DRAW);const location=gl.getAttribLocation(program,'aPosition');gl.enableVertexAttribArray(location);gl.vertexAttribPointer(location,2,gl.FLOAT,false,0,0);}
 const uniforms=new Map(),uniform=(name,...args)=>{if(!uniforms.has(name))uniforms.set(name,gl.getUniformLocation(program,name));const location=uniforms.get(name);if(location===null)return;if(args[0] instanceof Float32Array)gl.uniform3fv(location,args[0]);else gl['uniform'+args.length+'f'](location,...args);};
 function resize(){const r=canvas.getBoundingClientRect();canvas.width=Math.max(1,Math.round(Math.min(r.width,850)));canvas.height=Math.max(1,Math.round(canvas.width/(r.width/Math.max(1,r.height))));gl.viewport(0,0,canvas.width,canvas.height);uniform('uResolution',canvas.width,canvas.height);repaint();}
 const observer=new ResizeObserver(resize);observer.observe(canvas);cleanups.push(()=>{observer.disconnect();gl.deleteBuffer(buffer);gl.deleteProgram(program);gl.deleteShader(vs);gl.deleteShader(fs);});resize();return {gl,program,buffer,uniform,resize};}
try{
const coverPalettes={citrus:{label:'Electric citrus',accent:'#e8ff69',secondary:'#ff815c',dark:'#151926'},violet:{label:'Ultraviolet',accent:'#dda3ff',secondary:'#81f0d4',dark:'#1c1432'},tidal:{label:'Tidal blue',accent:'#83e8f6',secondary:'#ffb29b',dark:'#102935'}};
let coverState={caption:'AFTERGLOW',accent:'citrus',effect:'Default',revision:0};
const coverCanvas=$('[data-cover-canvas]'),coverBuffer=document.createElement('canvas');
let coverDrawingFailed=false,coverContext=null,coverBufferContext=null;
function projectCoverModel(input){const accent=Object.hasOwn(coverPalettes,input.accent)?input.accent:'citrus',caption=String(input.caption??''),effect=input.effect==='Pixelated'?'Pixelated':'Default';return {caption,accent,effect,revision:input.revision,palette:coverPalettes[accent],empty:caption.trim().length===0};}
function commitCoverState(patch){const next=projectCoverModel({...coverState,...patch,revision:coverState.revision+1});coverState={caption:next.caption,accent:next.accent,effect:next.effect,revision:next.revision};}
function renderCoverDOM(model){setText('[data-cover-caption]',model.empty?'No caption set':model.caption);setText('[data-cover-description]',model.palette.label+' · '+model.effect+' treatment');$('.cover-lab').style.setProperty('--cover-accent',model.palette.accent);$('[data-cover-dom]').dataset.modelRevision=String(model.revision);$('[data-cover-dom]').dataset.empty=String(model.empty);}
function coverBufferPlan(width,height,dpr,effect){const cssWidth=Math.max(1,width),cssHeight=Math.max(1,height),ratio=Math.max(1,Math.min(Number(dpr)||1,2)),pixelated=effect==='Pixelated';return {cssWidth,cssHeight,width:Math.max(1,Math.round(cssWidth*ratio)),height:Math.max(1,Math.round(cssHeight*ratio)),sourceWidth:Math.max(1,Math.round(cssWidth*(pixelated?.17:ratio))),sourceHeight:Math.max(1,Math.round(cssHeight*(pixelated?.17:ratio)))};}
function drawCoverArtwork(ctx,w,h,model){
 const p=model.palette,cx=w*.55,cy=h*.42,r=Math.min(w*.29,h*.30);
 ctx.fillStyle=p.dark;ctx.fillRect(0,0,w,h);
 const wash=ctx.createLinearGradient(0,0,w,h);wash.addColorStop(0,p.dark);wash.addColorStop(1,'#422b4b');ctx.fillStyle=wash;ctx.fillRect(0,0,w,h);
 ctx.fillStyle='#ffffff50';for(let i=0;i<74;i++){const x=((i*127+39)%997)/997*w,y=((i*i*19+61)%751)/751*h;ctx.fillRect(x,y,Math.max(.7,w*.0015),Math.max(.7,w*.0015));}
 ctx.save();ctx.translate(cx,cy);ctx.rotate(-.38);
 const ringColor=ctx.createLinearGradient(-r*1.6,-r,r*1.6,r);ringColor.addColorStop(0,p.secondary);ringColor.addColorStop(.43,p.accent);ringColor.addColorStop(.72,'#fffde1');ringColor.addColorStop(1,p.secondary);
 for(let i=0;i<37;i++){const progress=i/36;ctx.globalAlpha=.18+progress*.6;ctx.strokeStyle=ringColor;ctx.lineWidth=Math.max(.7,r*.012);ctx.beginPath();ctx.ellipse(0,(progress-.5)*r*.15,r*(1.08+progress*.66),r*(.35+progress*.27),0,0,Math.PI*2);ctx.stroke();}
 ctx.globalAlpha=1;ctx.restore();
 const globe=ctx.createRadialGradient(cx-r*.35,cy-r*.4,0,cx,cy,r);globe.addColorStop(0,'#fffddb');globe.addColorStop(.28,p.accent);globe.addColorStop(.78,p.secondary);globe.addColorStop(1,'#583249');ctx.fillStyle=globe;ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.fill();
 ctx.save();ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.clip();
 for(let i=0;i<58;i++){const t=i/57,y=cy-r+t*r*2;ctx.strokeStyle=i%3===0?'#fff8daaa':i%3===1?p.secondary:'#20244077';ctx.lineWidth=r*.018;ctx.beginPath();for(let j=0;j<=35;j++){const x=cx-r+j/35*r*2,yy=y+Math.sin(j/35*5.8+t*5)*r*.16; j?ctx.lineTo(x,yy):ctx.moveTo(x,yy);}ctx.stroke();}
 ctx.restore();ctx.strokeStyle='#fffbd18a';ctx.lineWidth=Math.max(.8,r*.014);ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.stroke();
 ctx.textBaseline='alphabetic';ctx.fillStyle='#f9f5e7';let fontSize=Math.min(w*.115,h*.155);ctx.font='700 '+fontSize+'px Arial, sans-serif';if(!model.empty){const textWidth=ctx.measureText(model.caption).width;if(textWidth>w*.86){fontSize*=w*.86/textWidth;ctx.font='700 '+fontSize+'px Arial, sans-serif';}ctx.fillText(model.caption,w*.06,h*.805);}
 ctx.fillStyle=p.accent;ctx.fillRect(w*.06,h*.90,w*.055,h*.033);ctx.fillStyle='#eee7e3';ctx.font=Math.max(7,h*.028)+'px monospace';ctx.fillText(p.label.toUpperCase()+' / '+model.effect.toUpperCase(),w*.135,h*.926);
 ctx.strokeStyle='#fff4db44';ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(w*.06,h*.85);ctx.lineTo(w*.94,h*.85);ctx.stroke();
}
function paintCoverFrame(model){
 if(!coverContext)coverContext=coverCanvas.getContext('2d');if(!coverBufferContext)coverBufferContext=coverBuffer.getContext('2d');if(!coverContext||!coverBufferContext)throw Error('Canvas2D context creation was unavailable.');
 const rect=coverCanvas.getBoundingClientRect(),plan=coverBufferPlan(rect.width,rect.height,window.devicePixelRatio,model.effect);
 coverCanvas.width=plan.width;coverCanvas.height=plan.height;coverBuffer.width=plan.sourceWidth;coverBuffer.height=plan.sourceHeight;
 coverBufferContext.setTransform(plan.sourceWidth/plan.cssWidth,0,0,plan.sourceHeight/plan.cssHeight,0,0);drawCoverArtwork(coverBufferContext,plan.cssWidth,plan.cssHeight,model);
 coverContext.setTransform(1,0,0,1,0,0);coverContext.imageSmoothingEnabled=false;coverContext.drawImage(coverBuffer,0,0,plan.width,plan.height);
 coverCanvas.dataset.drawnRevision=String(model.revision);setText('[data-cover-render-status]',model.effect+' · source '+plan.sourceWidth+' × '+plan.sourceHeight+' → output '+plan.width+' × '+plan.height);
}
function showCoverFailure(error){coverDrawingFailed=true;coverCanvas.hidden=true;$('[data-cover-failure]').hidden=false;setText('[data-cover-failure-detail]','The drawing could not be completed. Your caption, palette and controls remain usable in the HTML card.');setText('[data-cover-render-status]','Illustration unavailable · semantic content remains editable');report(new Error('Independent cover illustration unavailable: '+(error instanceof Error?error.message:String(error))));}
function renderSharedCover(){const model=projectCoverModel(coverState);renderCoverDOM(model);if(!coverDrawingFailed){try{paintCoverFrame(model);}catch(error){showCoverFailure(error);}}if(!coverDrawingFailed)setText('[data-feedback]','This cover is stationary. Editing the shared values redraws both views.');}
for(const key of ['caption','accent','effect'])on($('[data-control="'+key+'"]'),'input',event=>commitCoverState({[key]:event.currentTarget.value}),{capture:true});
listen('reset-cover',()=>{commitCoverState({caption:'AFTERGLOW',accent:'citrus',effect:'Default'});for(const key of ['caption','accent','effect'])$('[data-control="'+key+'"]').value=coverState[key];repaint();});
const coverResize=new ResizeObserver(()=>repaint());coverResize.observe(coverCanvas);cleanups.push(()=>coverResize.disconnect());
install(renderSharedCover);

}catch(error){report(error);}
requestAnimationFrame(()=>requestAnimationFrame(()=>{if(preview.status!=='error'){preview.status='mounted';document.body.dataset.previewStatus='mounted';parent.postMessage({type:'sg-preview-ready',id},'*');observe();setTimeout(observe,700);}}));
})();</script></body></html>
styles.css독립 구현
파일 저장

/* Generated by scripts/build-codrops-article-studies.mjs.
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.

Scope: independently authored study HTML, CSS, JavaScript, shaders and SVG only. Original articles, source projects, brands, artwork and screenshots are excluded. Their licenses and acquisition dispositions are unchanged.
*/

*{box-sizing:border-box}html{color-scheme:dark}html,body{margin:0;min-height:100%}body{background:var(--paper,#0c1321);color:var(--ink,#f4f3e9);font-family:Arial,Helvetica,sans-serif}button,input,select{font:inherit}button,select,input{min-width:0}button{background:transparent;border:1px solid #ffffff40;border-radius:999px;color:inherit;cursor:pointer;min-height:40px;padding:10px 16px}button:hover{background:#ffffff15}button[aria-pressed=true]{background:var(--accent,#dbff78);border-color:transparent;color:#122014}button:disabled{cursor:default;opacity:.6}button:focus-visible,input:focus-visible,select:focus-visible,summary:focus-visible,a:focus-visible{outline:3px solid var(--accent,#dbff78);outline-offset:4px}input,select{accent-color:var(--accent,#dbff78)}select,input[type=text],input[type=number]{background:#132235;border:1px solid #ffffff50;border-radius:7px;color:#fff;min-height:38px;padding:8px;width:100%}input[type=range]{display:block;min-height:30px;width:100%}a{color:inherit}svg{display:block;max-width:100%}h1,h2,p,figure{margin:0}h1{font-size:clamp(36px,6.5vw,78px);font-weight:500;letter-spacing:-.06em;line-height:.99}h2{font-size:24px;font-weight:500;letter-spacing:-.025em}p{line-height:1.55}.study{isolation:isolate;min-height:100vh;padding:clamp(18px,3.8vw,48px)}.masthead{align-items:center;display:flex;flex-wrap:wrap;font:10px/1.5 ui-monospace,monospace;gap:16px;justify-content:space-between;letter-spacing:.11em;margin-bottom:35px;text-transform:uppercase}.masthead strong{font-size:12px;letter-spacing:-.02em}.intro{display:grid;gap:18px;grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);margin-bottom:30px;align-items:end}.intro p{color:var(--muted,#b7c7d4);font-size:13px;max-width:38em}.eyebrow{color:var(--accent,#dbff78);font:10px/1.4 ui-monospace,monospace;letter-spacing:.14em;margin-bottom:12px;text-transform:uppercase}.workbench{display:grid;gap:20px;grid-template-columns:minmax(0,1fr) 230px}.visual{background:var(--surface,#101d2b);border:1px solid #ffffff20;border-radius:20px;min-width:0;overflow:hidden;position:relative}.drawing{height:auto;width:100%}.art-canvas{aspect-ratio:1.55;display:block;height:auto;width:100%}.panel{align-content:start;display:grid;gap:17px}.control{display:grid;font-size:11px;gap:7px;min-width:0}.control>span{display:flex;gap:10px;justify-content:space-between}.control output{color:var(--accent,#dbff78);font-family:ui-monospace,monospace}.controls{display:flex;flex-wrap:wrap;gap:8px}.readout{background:#ffffff09;border:1px solid #ffffff18;border-radius:12px;font:11px/1.7 ui-monospace,monospace;overflow-wrap:anywhere;padding:14px;white-space:pre-line}.legend{align-items:center;display:flex;flex-wrap:wrap;font:10px/1.6 ui-monospace,monospace;gap:16px;padding:16px 20px}.legend i{background:var(--accent,#dbff78);border-radius:50%;display:inline-block;height:6px;margin-right:6px;width:6px}.contract{border-top:1px solid #ffffff20;color:var(--muted,#b7c7d4);font-size:11px;margin-top:28px;padding-top:18px}.contract summary{color:var(--ink,#f4f3e9);cursor:pointer;font-size:12px}.contract p{margin-top:10px;max-width:90ch}.motion-bar{align-items:center;border-top:1px solid #ffffff20;display:flex;flex-wrap:wrap;font-size:11px;gap:12px;justify-content:space-between;margin-top:26px;padding-top:18px}.motion-bar label{align-items:center;display:flex;gap:8px}.feedback{color:var(--muted,#b7c7d4);font:11px/1.5 ui-monospace,monospace;min-height:18px}.visually-hidden{clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;width:1px}[hidden]{display:none!important}.stage-note{background:#07111ac9;bottom:18px;color:#dfe8f0;font:11px/1.5 ui-monospace,monospace;left:18px;max-width:calc(100% - 36px);padding:8px 12px;position:absolute;z-index:2}.big-number{font-size:clamp(70px,14vw,180px);letter-spacing:-.08em;line-height:1}.plain-list{display:grid;gap:12px;list-style:none;margin:0;padding:0}.plain-list li{border-bottom:1px solid #ffffff20;padding:12px 0}.tabs{display:flex;flex-wrap:wrap;gap:7px}.tabs button{font-size:11px}.two{display:grid;gap:20px;grid-template-columns:1fr 1fr}.code-sample{color:#c9ecbe;font:11px/1.7 ui-monospace,monospace;overflow:auto;white-space:pre-wrap}.fallback{padding:30px}.fallback p{max-width:38ch}.static-gallery{display:none}.study[data-reduced=true] .static-gallery{display:grid}.study[data-reduced=true] .animated-story{display:none}.study[data-reduced=true] *{animation:none!important;transition:none!important}body[data-preview-status=error] .feedback{color:#ffbba3}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;scroll-behavior:auto!important;transition:none!important}}@media(max-width:700px){.intro{grid-template-columns:1fr}.workbench{grid-template-columns:1fr}.panel{grid-template-columns:repeat(2,minmax(0,1fr))}.panel>.readout,.panel>.controls{grid-column:1/-1}.masthead{margin-bottom:26px}.two{grid-template-columns:1fr}}@media(max-width:380px){.panel{gap:12px}.control{font-size:10px}.panel button{font-size:11px;padding:9px 12px}.legend{gap:8px;padding:12px}.visual{border-radius:13px}}


.cover-lab{--cover-accent:#e8ff69;background:#161426}.cover-painted{position:relative}.cover-canvas{aspect-ratio:1.38;display:block;height:auto;width:100%}.cover-surface-label{background:#191927cc;border:1px solid #ffffff30;border-radius:20px;color:#f2efde;font:8px/1.4 ui-monospace,monospace;left:14px;letter-spacing:.08em;padding:5px 8px;position:absolute;top:13px}.cover-dom{align-items:center;background:#f1efe4;border-top:5px solid var(--cover-accent);color:#292633;display:flex;gap:18px;justify-content:space-between;padding:20px 22px}.cover-dom>div{min-width:0}.cover-dom-label{color:#615e65;font:8px/1.4 ui-monospace,monospace;letter-spacing:.1em}.cover-dom h2{font-size:clamp(21px,2.8vw,32px);font-weight:700;letter-spacing:-.04em;line-height:1.1;margin-top:8px;overflow-wrap:anywhere;white-space:pre-wrap}.cover-dom-description{font:10px/1.5 ui-monospace,monospace;margin-top:8px}.cover-accent-swatch{background:var(--cover-accent);border:1px solid #29263333;border-radius:50%;box-shadow:inset -10px -5px 0 #29263314;flex:none;height:42px;width:42px}.cover-controls .control small{color:#adb0c1;font-size:9px;font-weight:400}.cover-help{color:#b7b8c7;font-size:11px}.cover-render-status{border-top:1px solid #ffffff22;color:#d9d9c8;font:9px/1.6 ui-monospace,monospace;padding-top:12px}.cover-failure{align-content:center;color:#f4e8d3;display:grid;gap:14px;min-height:240px;padding:32px}.cover-failure p{font-size:12px;max-width:40ch}.cover-failure strong{font-size:24px;font-weight:500}@media(max-width:700px){.cover-caption-input,.cover-controls>.cover-help,.cover-controls>.cover-render-status{grid-column:1/-1}.cover-canvas{aspect-ratio:1.1}.cover-dom{padding:17px}.cover-controls .controls{margin-top:0}}@media(max-height:550px) and (min-width:500px){.cover-workbench{gap:16px}.cover-canvas{aspect-ratio:auto;height:190px}.cover-dom{border-top-width:3px;gap:10px;padding:10px 14px}.cover-dom-label{font-size:6px}.cover-dom h2{font-size:19px;margin-top:4px}.cover-dom-description{font-size:8px;margin-top:4px}.cover-accent-swatch{height:28px;width:28px}.cover-surface-label{font-size:6px;left:10px;padding:4px 6px;top:9px}.cover-controls .control small{font-size:7px}.cover-controls .cover-help{font-size:9px;line-height:1.45}.cover-render-status{font-size:8px;padding-top:8px}}


@media(max-height:550px) and (min-width:500px){
.study{padding:18px}.masthead{font-size:8px;gap:10px;margin-bottom:16px}.masthead strong{font-size:10px}.intro{gap:18px;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);margin-bottom:18px}.intro h1{font-size:34px}.intro p{font-size:10px;line-height:1.5}.intro .eyebrow{font-size:8px;margin-bottom:8px}.workbench{gap:16px;grid-template-columns:minmax(0,1fr) 174px}.panel{gap:10px;grid-template-columns:1fr}.panel .control{font-size:9px;gap:3px}.panel .control input[type=range]{min-height:22px}.panel select,.panel input[type=text],.panel input[type=number]{font-size:10px;min-height:31px;padding:6px}.panel .controls{gap:5px}.panel button{font-size:9px;min-height:30px;padding:7px 10px}.panel .readout{font-size:8px;padding:10px}.art-canvas{aspect-ratio:auto!important;height:250px;object-fit:contain}.legend{font-size:8px;gap:8px;padding:10px 13px}.stage-note{bottom:12px;font-size:8px;left:12px;padding:5px 8px}.type-lab{min-height:280px;padding:23px}.type-heading{font-size:36px}.particle-rack{height:80px}.type-rule{font-size:7px}.bounce-lab>.drawing{height:230px;object-fit:contain}.track-graph{padding:9px 16px}.track-graph svg{height:43px}.member-lab{min-height:300px;padding:18px}.member-heading h2{font-size:24px}.member-collage{height:180px}.member-total .counter{font-size:52px}.member-total>span{font-size:7px}.member-card span{font-size:6px;padding:4px}.composer-surface{padding:20px}.composer-label{font-size:8px;margin-bottom:20px}.form-set{gap:10px}.form-set h2{font-size:20px;margin-top:12px}.form-set p{font-size:9px}.timeline-rows{padding:12px 17px}.mascot-lab>.drawing{height:250px;object-fit:contain}.pose-strip{padding:10px 14px}.pose-marker{font-size:8px;padding:6px 0}.fit-stage{min-height:270px;padding:20px}.fit-stage canvas{max-height:235px}.tree-columns{grid-template-columns:125px minmax(0,1fr);min-height:320px}.tree-inspector{padding:17px 12px}.tree-inspector pre{font-size:8px;margin-top:18px}.tree-rules{display:none}.tree-preview{min-height:0;padding:14px}.tree-width-label,.tree-caption{font-size:7px}.fixture-copy h2{font-size:1.6em}.fixture-copy p{font-size:.65em}.fixture-output{gap:10px;padding:12px}.fixture-art{max-height:90px}.semantic-lab{grid-template-columns:110px minmax(0,1fr);min-height:320px}.node-list{flex-direction:column;padding:12px 7px}.node-list button{font-size:8px;gap:5px;padding:9px 7px}.node-list button small{font-size:7px}.semantic-preview{padding:14px}.semantic-fixture{padding:8px}.semantic-fixture h2{font-size:27px}.semantic-fixture p{font-size:10px}.semantic-art{height:75px}.markup-panel{padding:15px}.markup-panel pre{font-size:8px}.plant-lab{padding:18px}.plant-heading h2{font-size:28px}.plant-heading span{font-size:8px}.plant-illustration{margin:-60px 0 -8px}.plant-illustration svg{height:250px}.plant-callouts{gap:5px}.plant-callouts button{font-size:7px;min-height:28px;padding:7px}.plant-feature,.care-plan{font-size:10px}.orbit-lab{min-height:300px}.orbit-camera{height:270px}.orbit-ring{height:170px;width:110px}.orbit-card{height:170px;width:110px}.orbit-foot{font-size:8px;padding:10px 14px}.botanical-window{aspect-ratio:1.5}.window-label h2{font-size:29px}.window-label span{font-size:8px}.gold-lab{padding:18px}.gold-heading h2{font-size:28px;margin-top:9px}.gold-heading>span,.gold-total>span{font-size:7px}.gold-lab>svg{height:175px;margin:-27px 0 -6px}.gold-total{padding-top:9px}.gold-total output{font-size:43px}.gold-disclaimer{font-size:9px}.story-controls{gap:10px;grid-template-columns:repeat(4,minmax(0,1fr));padding:10px 0;position:relative}.story-controls .control{font-size:8px}.story-controls input[type=range]{min-height:20px}.story-controls>.controls{grid-column:auto}.story-controls button{font-size:8px;min-height:27px;padding:6px 8px}.story-pin{top:10px}.story-pin>.visual{min-height:270px}.story-pin .art-canvas{height:270px}.story-copy{bottom:20px;left:20px;max-width:255px}.story-chapter h2{font-size:29px;margin:8px 0}.story-chapter p{font-size:10px}.story-chapter>span{font-size:8px}.moon-lab{aspect-ratio:auto;min-height:270px}.moon-progress,.cylinder-energy,.terrain-coordinates{font-size:8px}.story-skip{font-size:9px;margin-bottom:10px}.reading-chapter{grid-template-columns:1fr 1fr}.motion-bar{margin-top:18px}
}
함께 쓰는 파일 5개 보기
script.js독립 구현
파일 저장

/* Generated by scripts/build-codrops-article-studies.mjs.
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.

Scope: independently authored study HTML, CSS, JavaScript, shaders and SVG only. Original articles, source projects, brands, artwork and screenshots are excluded. Their licenses and acquisition dispositions are unchanged.
*/
(() => {
'use strict';
const id="codrops-9e12943621d5",stage=document.querySelector('.study');
const preview=window.__STYLEGALLERY_PREVIEW__={id,status:'loading',errors:[]};
const report=error=>{const message=error instanceof Error?error.message:String(error);if(!preview.errors.includes(message))preview.errors.push(message);preview.status='error';document.body.dataset.previewStatus='error';parent.postMessage({type:'sg-preview-error',id,message},'*');const feedback=document.querySelector('[data-feedback]');if(feedback)feedback.textContent=message;};
addEventListener('error',event=>report(event.error||event.message));addEventListener('unhandledrejection',event=>report(event.reason));
const $=selector=>stage.querySelector(selector),$$=selector=>[...stage.querySelectorAll(selector)];
const value=key=>$('[data-control="'+key+'"]').value,number=key=>Number(value(key));
const clamp=(x,a=0,b=1)=>Math.max(a,Math.min(b,x)),lerp=(a,b,t)=>a+(b-a)*t;
const setText=(selector,text)=>{const node=$(selector);if(node)node.textContent=text;};
const media=matchMedia('(prefers-reduced-motion: reduce)');
let isReduced=media.matches,playing=false,time=0,draw=()=>{},raf=0,last=0,disposed=false;
const cleanups=[],on=(target,type,fn,options)=>{target.addEventListener(type,fn,options);cleanups.push(()=>target.removeEventListener(type,fn,options));};
const listen=(key,fn)=>on($('[data-action="'+key+'"]'),'click',fn);
const repaint=()=>{if(!disposed){try{draw(0);}catch(error){report(error);}}};
const loop=now=>{raf=0;if(disposed||!playing||isReduced)return;const dt=Math.min((now-last)/1000,.05);last=now;time+=dt;try{draw(dt);}catch(error){report(error);stop();return;}raf=requestAnimationFrame(loop);};
function stop(){playing=false;cancelAnimationFrame(raf);raf=0;$$('[data-play]').forEach(button=>{button.setAttribute('aria-pressed','false');button.textContent=isReduced?'Static view active':'Play motion';});}
function start(){if(isReduced)return;playing=true;last=performance.now();$$('[data-play]').forEach(button=>{button.setAttribute('aria-pressed','true');button.textContent='Pause motion';});if(!raf)raf=requestAnimationFrame(loop);}
function applyMotionPreference(){isReduced=media.matches||$('[data-static]').checked;stage.dataset.reduced=String(isReduced);if(isReduced)stop();$$('[data-play]').forEach(button=>{button.disabled=isReduced;button.textContent=playing?'Pause motion':isReduced?'Static view active':'Play motion';});repaint();}
function install(render){draw=render;applyMotionPreference();}
const observe=()=>{const root=document.getElementById('root'),elements=[...root.querySelectorAll('*')],rect=root.getBoundingClientRect();parent.postMessage({type:'sg-preview-observation',id,diagnostics:{textLength:root.textContent.trim().length,elementCount:elements.length,visibleElementCount:elements.filter(element=>{const r=element.getBoundingClientRect(),s=getComputedStyle(element);return r.width>0&&r.height>0&&s.display!=='none'&&s.visibility!=='hidden'&&Number(s.opacity)!==0;}).length,images:[],canvases:$$('canvas').map(c=>({width:c.width,height:c.height})),rootRect:{width:rect.width,height:rect.height},documentWidth:document.documentElement.scrollWidth,viewportWidth:innerWidth}},'*');};
on(media,'change',applyMotionPreference);on($('[data-static]'),'change',applyMotionPreference);
$$('[data-play]').forEach(button=>on(button,'click',()=>playing?stop():start()));
$$('[data-control]').forEach(input=>on(input,'input',()=>{const output=$('[data-value="'+input.dataset.control+'"]');if(output)output.textContent=input.value;if(input.dataset.control!=='story')repaint();}));
on(document,'visibilitychange',()=>{if(document.hidden)stop();});
on(window,'pagehide',()=>{disposed=true;stop();cleanups.forEach(fn=>fn());});
function canvas2D(selector,width=900,height=580){const canvas=$(selector);canvas.width=width;canvas.height=height;const context=canvas.getContext('2d');if(!context)throw Error('A 2D drawing surface is unavailable.');return {canvas,ctx:context,width,height};}
function createGL(canvas,fragment,vertex){const gl=canvas.getContext('webgl',{alpha:false,antialias:true,preserveDrawingBuffer:true});if(!gl){canvas.hidden=true;const fallback=$('[data-gl-fallback]');if(fallback)fallback.hidden=false;throw Error('WebGL is unavailable. The study explanation and controls remain readable.');}
 const shader=(type,source)=>{const s=gl.createShader(type);gl.shaderSource(s,source);gl.compileShader(s);if(!gl.getShaderParameter(s,gl.COMPILE_STATUS))throw Error(gl.getShaderInfoLog(s));return s;};
 const vs=shader(gl.VERTEX_SHADER,vertex||'attribute vec2 aPosition;varying vec2 vUv;void main(){vUv=aPosition*.5+.5;gl_Position=vec4(aPosition,0.,1.);}'),fs=shader(gl.FRAGMENT_SHADER,fragment),program=gl.createProgram();gl.attachShader(program,vs);gl.attachShader(program,fs);gl.linkProgram(program);if(!gl.getProgramParameter(program,gl.LINK_STATUS))throw Error(gl.getProgramInfoLog(program));gl.useProgram(program);
 const buffer=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,buffer);if(!vertex){gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),gl.STATIC_DRAW);const location=gl.getAttribLocation(program,'aPosition');gl.enableVertexAttribArray(location);gl.vertexAttribPointer(location,2,gl.FLOAT,false,0,0);}
 const uniforms=new Map(),uniform=(name,...args)=>{if(!uniforms.has(name))uniforms.set(name,gl.getUniformLocation(program,name));const location=uniforms.get(name);if(location===null)return;if(args[0] instanceof Float32Array)gl.uniform3fv(location,args[0]);else gl['uniform'+args.length+'f'](location,...args);};
 function resize(){const r=canvas.getBoundingClientRect();canvas.width=Math.max(1,Math.round(Math.min(r.width,850)));canvas.height=Math.max(1,Math.round(canvas.width/(r.width/Math.max(1,r.height))));gl.viewport(0,0,canvas.width,canvas.height);uniform('uResolution',canvas.width,canvas.height);repaint();}
 const observer=new ResizeObserver(resize);observer.observe(canvas);cleanups.push(()=>{observer.disconnect();gl.deleteBuffer(buffer);gl.deleteProgram(program);gl.deleteShader(vs);gl.deleteShader(fs);});resize();return {gl,program,buffer,uniform,resize};}
try{
const coverPalettes={citrus:{label:'Electric citrus',accent:'#e8ff69',secondary:'#ff815c',dark:'#151926'},violet:{label:'Ultraviolet',accent:'#dda3ff',secondary:'#81f0d4',dark:'#1c1432'},tidal:{label:'Tidal blue',accent:'#83e8f6',secondary:'#ffb29b',dark:'#102935'}};
let coverState={caption:'AFTERGLOW',accent:'citrus',effect:'Default',revision:0};
const coverCanvas=$('[data-cover-canvas]'),coverBuffer=document.createElement('canvas');
let coverDrawingFailed=false,coverContext=null,coverBufferContext=null;
function projectCoverModel(input){const accent=Object.hasOwn(coverPalettes,input.accent)?input.accent:'citrus',caption=String(input.caption??''),effect=input.effect==='Pixelated'?'Pixelated':'Default';return {caption,accent,effect,revision:input.revision,palette:coverPalettes[accent],empty:caption.trim().length===0};}
function commitCoverState(patch){const next=projectCoverModel({...coverState,...patch,revision:coverState.revision+1});coverState={caption:next.caption,accent:next.accent,effect:next.effect,revision:next.revision};}
function renderCoverDOM(model){setText('[data-cover-caption]',model.empty?'No caption set':model.caption);setText('[data-cover-description]',model.palette.label+' · '+model.effect+' treatment');$('.cover-lab').style.setProperty('--cover-accent',model.palette.accent);$('[data-cover-dom]').dataset.modelRevision=String(model.revision);$('[data-cover-dom]').dataset.empty=String(model.empty);}
function coverBufferPlan(width,height,dpr,effect){const cssWidth=Math.max(1,width),cssHeight=Math.max(1,height),ratio=Math.max(1,Math.min(Number(dpr)||1,2)),pixelated=effect==='Pixelated';return {cssWidth,cssHeight,width:Math.max(1,Math.round(cssWidth*ratio)),height:Math.max(1,Math.round(cssHeight*ratio)),sourceWidth:Math.max(1,Math.round(cssWidth*(pixelated?.17:ratio))),sourceHeight:Math.max(1,Math.round(cssHeight*(pixelated?.17:ratio)))};}
function drawCoverArtwork(ctx,w,h,model){
 const p=model.palette,cx=w*.55,cy=h*.42,r=Math.min(w*.29,h*.30);
 ctx.fillStyle=p.dark;ctx.fillRect(0,0,w,h);
 const wash=ctx.createLinearGradient(0,0,w,h);wash.addColorStop(0,p.dark);wash.addColorStop(1,'#422b4b');ctx.fillStyle=wash;ctx.fillRect(0,0,w,h);
 ctx.fillStyle='#ffffff50';for(let i=0;i<74;i++){const x=((i*127+39)%997)/997*w,y=((i*i*19+61)%751)/751*h;ctx.fillRect(x,y,Math.max(.7,w*.0015),Math.max(.7,w*.0015));}
 ctx.save();ctx.translate(cx,cy);ctx.rotate(-.38);
 const ringColor=ctx.createLinearGradient(-r*1.6,-r,r*1.6,r);ringColor.addColorStop(0,p.secondary);ringColor.addColorStop(.43,p.accent);ringColor.addColorStop(.72,'#fffde1');ringColor.addColorStop(1,p.secondary);
 for(let i=0;i<37;i++){const progress=i/36;ctx.globalAlpha=.18+progress*.6;ctx.strokeStyle=ringColor;ctx.lineWidth=Math.max(.7,r*.012);ctx.beginPath();ctx.ellipse(0,(progress-.5)*r*.15,r*(1.08+progress*.66),r*(.35+progress*.27),0,0,Math.PI*2);ctx.stroke();}
 ctx.globalAlpha=1;ctx.restore();
 const globe=ctx.createRadialGradient(cx-r*.35,cy-r*.4,0,cx,cy,r);globe.addColorStop(0,'#fffddb');globe.addColorStop(.28,p.accent);globe.addColorStop(.78,p.secondary);globe.addColorStop(1,'#583249');ctx.fillStyle=globe;ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.fill();
 ctx.save();ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.clip();
 for(let i=0;i<58;i++){const t=i/57,y=cy-r+t*r*2;ctx.strokeStyle=i%3===0?'#fff8daaa':i%3===1?p.secondary:'#20244077';ctx.lineWidth=r*.018;ctx.beginPath();for(let j=0;j<=35;j++){const x=cx-r+j/35*r*2,yy=y+Math.sin(j/35*5.8+t*5)*r*.16; j?ctx.lineTo(x,yy):ctx.moveTo(x,yy);}ctx.stroke();}
 ctx.restore();ctx.strokeStyle='#fffbd18a';ctx.lineWidth=Math.max(.8,r*.014);ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.stroke();
 ctx.textBaseline='alphabetic';ctx.fillStyle='#f9f5e7';let fontSize=Math.min(w*.115,h*.155);ctx.font='700 '+fontSize+'px Arial, sans-serif';if(!model.empty){const textWidth=ctx.measureText(model.caption).width;if(textWidth>w*.86){fontSize*=w*.86/textWidth;ctx.font='700 '+fontSize+'px Arial, sans-serif';}ctx.fillText(model.caption,w*.06,h*.805);}
 ctx.fillStyle=p.accent;ctx.fillRect(w*.06,h*.90,w*.055,h*.033);ctx.fillStyle='#eee7e3';ctx.font=Math.max(7,h*.028)+'px monospace';ctx.fillText(p.label.toUpperCase()+' / '+model.effect.toUpperCase(),w*.135,h*.926);
 ctx.strokeStyle='#fff4db44';ctx.lineWidth=1;ctx.beginPath();ctx.moveTo(w*.06,h*.85);ctx.lineTo(w*.94,h*.85);ctx.stroke();
}
function paintCoverFrame(model){
 if(!coverContext)coverContext=coverCanvas.getContext('2d');if(!coverBufferContext)coverBufferContext=coverBuffer.getContext('2d');if(!coverContext||!coverBufferContext)throw Error('Canvas2D context creation was unavailable.');
 const rect=coverCanvas.getBoundingClientRect(),plan=coverBufferPlan(rect.width,rect.height,window.devicePixelRatio,model.effect);
 coverCanvas.width=plan.width;coverCanvas.height=plan.height;coverBuffer.width=plan.sourceWidth;coverBuffer.height=plan.sourceHeight;
 coverBufferContext.setTransform(plan.sourceWidth/plan.cssWidth,0,0,plan.sourceHeight/plan.cssHeight,0,0);drawCoverArtwork(coverBufferContext,plan.cssWidth,plan.cssHeight,model);
 coverContext.setTransform(1,0,0,1,0,0);coverContext.imageSmoothingEnabled=false;coverContext.drawImage(coverBuffer,0,0,plan.width,plan.height);
 coverCanvas.dataset.drawnRevision=String(model.revision);setText('[data-cover-render-status]',model.effect+' · source '+plan.sourceWidth+' × '+plan.sourceHeight+' → output '+plan.width+' × '+plan.height);
}
function showCoverFailure(error){coverDrawingFailed=true;coverCanvas.hidden=true;$('[data-cover-failure]').hidden=false;setText('[data-cover-failure-detail]','The drawing could not be completed. Your caption, palette and controls remain usable in the HTML card.');setText('[data-cover-render-status]','Illustration unavailable · semantic content remains editable');report(new Error('Independent cover illustration unavailable: '+(error instanceof Error?error.message:String(error))));}
function renderSharedCover(){const model=projectCoverModel(coverState);renderCoverDOM(model);if(!coverDrawingFailed){try{paintCoverFrame(model);}catch(error){showCoverFailure(error);}}if(!coverDrawingFailed)setText('[data-feedback]','This cover is stationary. Editing the shared values redraws both views.');}
for(const key of ['caption','accent','effect'])on($('[data-control="'+key+'"]'),'input',event=>commitCoverState({[key]:event.currentTarget.value}),{capture:true});
listen('reset-cover',()=>{commitCoverState({caption:'AFTERGLOW',accent:'citrus',effect:'Default'});for(const key of ['caption','accent','effect'])$('[data-control="'+key+'"]').value=coverState[key];repaint();});
const coverResize=new ResizeObserver(()=>repaint());coverResize.observe(coverCanvas);cleanups.push(()=>coverResize.disconnect());
install(renderSharedCover);

}catch(error){report(error);}
requestAnimationFrame(()=>requestAnimationFrame(()=>{if(preview.status!=='error'){preview.status='mounted';document.body.dataset.previewStatus='mounted';parent.postMessage({type:'sg-preview-ready',id},'*');observe();setTimeout(observe,700);}}));
})();
LICENSE독립 구현
파일 저장

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.

Scope: independently authored study HTML, CSS, JavaScript, shaders and SVG only. Original articles, source projects, brands, artwork and screenshots are excluded. Their licenses and acquisition dispositions are unchanged.
NOTES.md독립 구현
파일 저장

# One state. Two surfaces.

Independent article case study. Domain: website consumer / Design Engineering presentation.

One application state owns caption, accent and effect. Both a semantic HTML card and an independently drawn Canvas2D cover consume the same snapshot. Pixelated mode paints that cover into a smaller detached canvas, then enlarges it with image smoothing disabled. Inputs and resize events redraw the current state without replacing native controls. This is manual drawing of known content, not automatic HTML rasterization.

## Attribution and evidence

Studied: [Exploring the HTML-in-Canvas Proposal](https://tympanus.net/codrops/2026/05/13/exploring-the-html-in-canvas-proposal/), Vittorio Retrivi. Article SHA-256: bbcd3d5a6b1b518988c4f5f67130c5c7dc17159278dafc7a57edcfd25a70b87f. Analysis SHA-256: 8f83d4ee6c2ef3e3087f05791568a0d76d6fc5d91844ca5e16ec0c0044196bc7. Source revision: 169917e07fce9566d6105807e474986a89de72f4ec1cd6f65e0c637fa8f6409c. The revision hashes the article/analysis hashes, the analyst principle and code-block evidence references, plus pinned original source/line hashes and the implementation contract, the exact original failure evidence, and pinned proposal context; it identifies studied evidence, not a claim that the original demo executes.

Original acquisition: acquired. Original license: MIT. originalSourceAcquired: true. Source code acquisition is complete. Original runtime remains failed-in-recorded-capability-environment: The exact compiled getting-started preview emits Uncaught TypeError: canvas.requestPaint is not a function; no ready event was received and the captured frame is white.

No original article prose, code, artwork, source project or screenshot is distributed here. The authored-code MIT notice applies only to the independent implementation. The acquired original failed in the recorded environment because requestPaint was not callable; its success was not verified. This example does not implement the HTML-in-Canvas proposal, copy arbitrary HTML/CSS, transform DOM hit testing, or reproduce the original Fluid/Rain shaders. The duplicate canvas is hidden from the accessibility tree; visible semantic content and native controls remain outside it. No assistive-technology conformance is claimed. Caption input is bounded to 28 code units by the native field. Empty captions keep the artwork visible. Long captions shrink to fit the painted line and wrap in the semantic card. Both presentation modes are stationary. A Canvas2D failure keeps the DOM representation usable and emits an error instead of readiness.

## Initial state and controls

Make a cover, then break it into pixels. The painted artwork and readable HTML share your caption and palette; the controls stay clear.

The page starts stationary. This cover owns caption, accent and effect in one state object. Input events update that object before redraw; reset restores all three inputs. Default uses a full-resolution Canvas2D buffer; Pixelated uses a smaller buffer enlarged without smoothing. Empty captions keep the geometry visible and show No caption set in the semantic card. Drawing failure retains editable DOM content and suppresses successful readiness. No continuous animation is scheduled. Play motion, where present, advances this study’s presentation until paused; text, bounce, portrait, orbit, strand and mascot motion loops, while the event composer stops at its endpoint. Scroll studies advance through ordinary document scroll or their chapter buttons. Static view stops time-based motion; manual controls still inspect values. The three scroll studies instead show every chapter in ordinary reading flow. Pointer parallax centers its decorative layers.

Initial control values:

| Control key | Value |
| --- | --- |
| caption | AFTERGLOW |
| accent | citrus |
| effect | Default |

## Running and editing

Open index.html directly. It contains all runtime code and visuals and needs no package install, account, network, remote asset or browser-origin storage. Edit its inline style and script to change the downloaded page. styles.css and script.js are reference copies of the inline source; changing those copies alone does not change the running page. Full repository regeneration overwrites index.html and all generated copies, including any manual edits. The --only=<baseline ID> option generates selected records while verifying and preserving existing records and downloads. Keep a separate copy when editing a download. The repository authoring modules in vendor/codrops-article-studies/scenes-*.mjs and runtime.mjs regenerate these copies through node scripts/build-codrops-article-studies.mjs. Repository regeneration also needs the private source evidence; downloaded-page execution does not.

## Verification boundary

This generation passed the captured article/analysis hash checks, article-code/paragraph pointer hashes, pinned source/line hashes, record/license/receipt hashes and original runtime snapshot hash, exact ID membership, local canonical Markdown anchor/hash checks, implementation-symbol checks, JavaScript parsing, semantic main/title counts, duplicate-ID checks and runtime URL/storage exclusion. The manifest codeFiles entries identify every emitted file by SHA-256. Each code symbol is a searchable named function, not a claimed executed call trace. Proposed checks are in KNOWLEDGE.json. runtime_verified: false. SG artifact capture proves only a recorded initial render; no interactive runtime, keyboard, accessibility, comfort or performance result is claimed.

Manual Chrome interaction is blocked by extension UI in this session. No alternate browser-control route was used. Preview sizing is native responsive width. Initial-render captures are tracked separately at site/artifacts/full-import/preview-posters/index.json and site/artifacts/full-import/preview-posters/visual-review.json in the repository. No capture receipt is bundled with this download. The bounded initial-render matrix requests all 22 studies at 320 pixels and three representative studies at 1280 pixels; the separate poster surface is 640 × 420. The authored CSS also adapts at 375 and 768 pixels, without claiming those widths were captured. A width request does not mean that capture exists or passed. Motion starts on explicit playback; system reduced motion and the Static view switch stop it.

consumer_reference: consumer-reference/agent-native/registry.json
KNOWLEDGE.json독립 구현
파일 저장

[
  {
    "id": "codrops-9e12943621d5-article-0",
    "source": "design-engineering/state-management/patterns/derived-state.md",
    "anchor": "state-model-and-ownership",
    "sourceSha256": "22192b2fbab970a71ec0237221dc2d35c578e59723bc0032c2347f8ef595cd6c",
    "title": "22 / Semantic content & pixel treatment · renderSharedCover",
    "principle": "A caption, accent and presentation mode have one writable state owner. The DOM and pixel renderers receive the same projected snapshot; current captions and colors are never stored as independent output state.",
    "application": "One application state owns caption, accent and effect. Both a semantic HTML card and an independently drawn Canvas2D cover consume the same snapshot. Pixelated mode paints that cover into a smaller detached canvas, then enlarges it with image smoothing disabled. Inputs and resize events redraw the current state without replacing native controls. This is manual drawing of known content, not automatic HTML rasterization. This binding describes the authored study and does not verify the original project.",
    "code": [
      {
        "file": "vendor/codrops-article-studies/records/codrops-9e12943621d5/script.js",
        "symbol": "renderSharedCover",
        "explanation": "Projects coverState once and passes that same snapshot to renderCoverDOM and paintCoverFrame. Capture-phase input handlers commit source values before the shared input handler requests the redraw."
      }
    ],
    "checks": [
      {
        "id": "codrops-9e12943621d5-renderSharedCover",
        "action": "Type NIGHT FIELD, choose Ultraviolet, switch Pixelated, then Reset cover. Repeat several rapid caption edits.",
        "expected": "After each draw, both representations use the final caption and palette. Reset restores AFTERGLOW, Electric citrus and Default together; the native field is not remounted."
      }
    ],
    "runtime_verified": false
  },
  {
    "id": "codrops-9e12943621d5-article-1",
    "source": "motion/rendering-and-performance.md",
    "anchor": "match-the-mechanism-to-the-responsibility",
    "sourceSha256": "1523fdeaa290fa064c4db17d36040de3f35ad39bb18a37517ceb5995f97d1dfd",
    "title": "22 / Semantic content & pixel treatment · paintCoverFrame",
    "principle": "Pixel treatment belongs to the separately drawn canvas. CSS dimensions, backing-buffer dimensions and the smaller pixelation source are explicit; input and resize events redraw the latest model without a continuous animation loop.",
    "application": "One application state owns caption, accent and effect. Both a semantic HTML card and an independently drawn Canvas2D cover consume the same snapshot. Pixelated mode paints that cover into a smaller detached canvas, then enlarges it with image smoothing disabled. Inputs and resize events redraw the current state without replacing native controls. This is manual drawing of known content, not automatic HTML rasterization. This binding describes the authored study and does not verify the original project.",
    "code": [
      {
        "file": "vendor/codrops-article-studies/records/codrops-9e12943621d5/script.js",
        "symbol": "paintCoverFrame",
        "explanation": "Reads the CSS bounds, derives explicit output and source dimensions with coverBufferPlan, paints owned geometry/text into the detached buffer, copies it with smoothing disabled, then records the drawn revision."
      }
    ],
    "checks": [
      {
        "id": "codrops-9e12943621d5-paintCoverFrame",
        "action": "Compare Default and Pixelated with the same caption and accent, then resize the preview.",
        "expected": "The original drawing becomes coarse in Pixelated mode; HTML text remains sharp. Source/output dimensions follow the new size, and drawnRevision is written only after drawing and copying the buffer complete."
      }
    ],
    "runtime_verified": false
  },
  {
    "id": "codrops-9e12943621d5-article-2",
    "source": "design-engineering/component-contract.md",
    "anchor": "contract-template",
    "sourceSha256": "79e496160e28ab26397d2c5b68075fd14c732d13002ff6c6e41c031d86cac039",
    "title": "22 / Semantic content & pixel treatment · showCoverFailure",
    "principle": "An empty caption is valid editable content. An unavailable drawing context is a different state that preserves semantic content and native controls while reporting a renderer failure.",
    "application": "One application state owns caption, accent and effect. Both a semantic HTML card and an independently drawn Canvas2D cover consume the same snapshot. Pixelated mode paints that cover into a smaller detached canvas, then enlarges it with image smoothing disabled. Inputs and resize events redraw the current state without replacing native controls. This is manual drawing of known content, not automatic HTML rasterization. This binding describes the authored study and does not verify the original project.",
    "code": [
      {
        "file": "vendor/codrops-article-studies/records/codrops-9e12943621d5/script.js",
        "symbol": "showCoverFailure",
        "explanation": "Latches drawing failure, hides the failed canvas, exposes the specific fallback and calls the existing error reporter. Later renderSharedCover calls still update semantic content while skipping further drawing attempts."
      }
    ],
    "checks": [
      {
        "id": "codrops-9e12943621d5-showCoverFailure",
        "action": "Clear the caption, then restore it. In a controlled failing-renderer check, make Canvas2D creation or painting throw and continue editing.",
        "expected": "An empty caption leaves the art visible and displays No caption set in HTML. A drawing failure shows Illustration unavailable, keeps DOM updates working, and prevents an independent ready signal for an undrawn canvas."
      }
    ],
    "runtime_verified": false
  },
  {
    "id": "codrops-9e12943621d5-article-3",
    "source": "motion/accessible-motion.md",
    "anchor": "preference-changes-during-work",
    "sourceSha256": "db0ad692d5bb3103a3cbe13ec08dbe5cf12552aed7ecf2c93b1d7cc0b8a34b8b",
    "title": "22 / Semantic content & pixel treatment · applyMotionPreference",
    "principle": "This example has no autonomous animation. Preference changes retain the same readable content and permit explicit edits in either stationary presentation mode.",
    "application": "One application state owns caption, accent and effect. Both a semantic HTML card and an independently drawn Canvas2D cover consume the same snapshot. Pixelated mode paints that cover into a smaller detached canvas, then enlarges it with image smoothing disabled. Inputs and resize events redraw the current state without replacing native controls. This is manual drawing of known content, not automatic HTML rasterization. This binding describes the authored study and does not verify the original project.",
    "code": [
      {
        "file": "vendor/codrops-article-studies/records/codrops-9e12943621d5/script.js",
        "symbol": "applyMotionPreference",
        "explanation": "Combines the system preference with the Static view checkbox and requests a redraw. This scene supplies no playback control or autonomous animation, so all content remains a stationary projection."
      }
    ],
    "checks": [
      {
        "id": "codrops-9e12943621d5-applyMotionPreference",
        "action": "Enable Static view, edit the caption and palette, then switch Default/Pixelated.",
        "expected": "The selected values remain inspectable and both views update only when an input or size changes; no continuous playback begins."
      }
    ],
    "runtime_verified": false
  }
]
RETRIEVAL-LOG.md독립 구현
파일 저장

# Retrieval log

Understood as: preserve all 22 original catalog IDs and acquisition dispositions while independently implementing each article’s technical principle as a consumer-owned case study.

SG discover and workflow read from site-compiler revision 6deb1970c80ed113b90be8d6ca617616bbe336c4; workflow SHA-256 0318a99707293af423e52b99b40cf1d87d9c7725e3ab06c5a231e3946f2bebb9. Stage 3 handoff is the primary implementation brief. No original scene assets or source files are reused.

Read-only canonical traversal:
1. AGENTS.md
2. README.md
3. DOMAINS.md
4. GOVERNANCE.md
5. design-engineering/index.md
6. motion/index.md
7. design-engineering/component-contract.md
8. design-engineering/state-management/patterns/derived-state.md
9. design-engineering/native-interaction-contracts.md
10. motion/accessible-motion.md
11. motion/rendering-and-performance.md
12. motion/interruption-and-retargeting.md
13. motion/interaction-recipes.md

Applied canonical sections and current-authoring hashes are recorded in KNOWLEDGE.json. No design-terminology records are relied on. External references within canonical guidance were not independently rechecked by this implementation lane; only the local sections and article capture identities were checked. Root Layout corpus, Game UI, provider integrations and publisher export claims were rejected as outside this lane.