<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>웹솔루션개발 26년 노하우! 해피CGI의 모든것</title>
    <link>https://happycgi.tistory.com/</link>
    <description>웹솔루션개발 26년 노하우! 해피CGI의 모든것</description>
    <language>ko</language>
    <pubDate>Sat, 23 May 2026 12:52:03 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>해피CGI윤실장</managingEditor>
    <image>
      <title>웹솔루션개발 26년 노하우! 해피CGI의 모든것</title>
      <url>https://t1.daumcdn.net/cfile/tistory/2156DC4B580719FD05</url>
      <link>https://happycgi.tistory.com</link>
    </image>
    <item>
      <title>[해피CGI][cgimall] 대량의 코퍼스에 기반한 일본어 이야기 작성 AI 플랫폼 AI Novelist</title>
      <link>https://happycgi.tistory.com/4038</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI Novelist 사용자가 예제 프롬프트나 자신의 텍스트에서 시작하여 AI를 사용하여 이야기를 쓸 수 있습니다. 참고로 일본어 작성됩니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;603&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8mlqS/dJMcab5mora/lqGxpuvqgnk2KxG9O942e0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8mlqS/dJMcab5mora/lqGxpuvqgnk2KxG9O942e0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8mlqS/dJMcab5mora/lqGxpuvqgnk2KxG9O942e0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8mlqS%2FdJMcab5mora%2FlqGxpuvqgnk2KxG9O942e0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;603&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;603&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;주요기능&amp;gt;&lt;br /&gt;- AI 기반 이야기 작성&lt;br /&gt;- 대형 일본어 모델&lt;br /&gt;- 영감을 위한 다양한 예제 프롬프트 제공&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;사용방법&amp;gt;&lt;br /&gt;회원가입 없이도 프롬프트로 기본 스토리 라인을 적으면 이야기를 생성해줍니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9KzCC/dJMcadWnPW6/4r1fVvqNkpVHf054oodnC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9KzCC/dJMcadWnPW6/4r1fVvqNkpVHf054oodnC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9KzCC/dJMcadWnPW6/4r1fVvqNkpVHf054oodnC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9KzCC%2FdJMcadWnPW6%2F4r1fVvqNkpVHf054oodnC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;700&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;가격정책&lt;br /&gt;- 프리 체험플랜 : 0원 월 5,000자 생성&lt;br /&gt;- 스탠다드 입문작가용: 월 10달러 ~ 15달러 : 월 10만 자 생성&lt;br /&gt;- 프로 전업 작가용 : 월 25달러 ~ 30달러 : 무제한 생서, 최신형 AI 모델 사용, 무제한 세계관 관리&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;전문이든 재미든 인공지원과 함께 일본어로 된 소설을 쓰고 싶으면 AI가 보조 혹은 주체가 되어 잘 쓸 수 있도록 도와주는 도구입니다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;&gt;&lt;a href=&quot;https://happycgi.com/program/detail.php?number=18146&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqkRTn/dJMcagexxOr/Zj1qw7PggkliIPQCT4wAVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqkRTn%2FdJMcagexxOr%2FZj1qw7PggkliIPQCT4wAVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;40&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹프로그램밍 자료실/AI 자료</category>
      <category>CGIMALL</category>
      <category>happycgi</category>
      <category>novel</category>
      <category>소설</category>
      <category>해피CGI</category>
      <author>해피CGI윤실장</author>
      <guid isPermaLink="true">https://happycgi.tistory.com/4038</guid>
      <comments>https://happycgi.tistory.com/4038#entry4038comment</comments>
      <pubDate>Fri, 22 May 2026 09:07:07 +0900</pubDate>
    </item>
    <item>
      <title>[해피CGI][cgimall] 부드러운 다크/라이트 모드 전환 라이브러리 - DayniteJS</title>
      <link>https://happycgi.tistory.com/4037</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lpFv3/dJMcadPzprj/P94QXoYSAIxGRHVvIZIKWk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lpFv3/dJMcadPzprj/P94QXoYSAIxGRHVvIZIKWk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lpFv3/dJMcadPzprj/P94QXoYSAIxGRHVvIZIKWk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/lpFv3/dJMcadPzprj/P94QXoYSAIxGRHVvIZIKWk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;215&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;215&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;■ 라이센스&lt;br /&gt;&lt;br /&gt;MIT 라이센스&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;■ 상세 설명&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Day/Nite는 웹사이트에서 라이트 모드와 다크 모드를 손쉽게 전환할 수 있도록 구현된 UI 컴포넌트이다.&lt;br /&gt;사용자의 환경 설정이나 직접 선택에 따라 테마를 변경할 수 있으며, CSS와 JavaScript를 조합하여 비교적 간단하게 적용할 수 있다.&lt;/div&gt;
&lt;div&gt;일반적으로 다크모드는 어두운 배경과 밝은 텍스트를 사용하여 눈의 피로를 줄이고 가독성을 높이기 위한 UI 방식이다.&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;이 예제는 다음과 같은 방식으로 동작한다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;- 토글 버튼 클릭 시 라이트 / 다크 모드 전환&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;-&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;CSS 클래스 또는 데이터 속성 변경을 통해 테마 적용&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;-&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;필요 시 localStorage를 활용하여 사용자 선택 상태 유지&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;또한 CSS의 prefers-color-scheme 미디어 쿼리를 활용하면&lt;/div&gt;
&lt;div&gt;사용자의 운영체제 설정(다크/라이트)에 맞춰 자동 적용도 가능하다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;구현 구조는 크게 다음과 같다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;HTML&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;-&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;토글 버튼 또는 스위치 UI 구성&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;CSS&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;-&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;라이트 / 다크 테마 스타일 정의&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;-&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;변수 또는 클래스 기반 스타일 분기 처리&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;JavaScript&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;-&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;클릭 이벤트로 테마 전환&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&amp;nbsp;-&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;선택 상태 저장 및 복원 처리&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;■ 주요 특징&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;다크모드 / 라이트모드 전환 지원&lt;/div&gt;
&lt;div&gt;간단한 구조로 빠른 적용 가능&lt;/div&gt;
&lt;div&gt;사용자 설정 저장(localStorage) 가능&lt;/div&gt;
&lt;div&gt;시스템 테마 자동 감지 가능&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;■ 활용 포인트&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;관리자 페이지 UI 개선&lt;/div&gt;
&lt;div&gt;사용자 편의성 향상 (야간 모드 지원)&lt;/div&gt;
&lt;div&gt;최신 웹 트렌드 대응&lt;/div&gt;
&lt;div&gt;디자인 테마 확장성 확보&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;■ 사용 기술&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;HTML&lt;/div&gt;
&lt;div&gt;CSS (prefers-color-scheme, 변수)&lt;/div&gt;
&lt;div&gt;JavaScript&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;■ 추천 사용 환경&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;홈페이지 및 쇼핑몰&lt;/div&gt;
&lt;div&gt;관리자 페이지&lt;/div&gt;
&lt;div&gt;SaaS / 웹솔루션 UI&lt;/div&gt;
&lt;div&gt;콘텐츠 사이트&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;br /&gt;■ 참고사항&lt;br /&gt;&lt;br /&gt;데모사이트에서 다운로드 받은 파일은 npm 환경에서만 구동됩니다.&amp;nbsp;&lt;br /&gt;본 글에 첨부된 파일을 다운로드 받으면&amp;nbsp;examples/test.html 파일이 있습니다.&amp;nbsp;&lt;br /&gt;해당 파일은 vanilla js 로 구성된 파일이므로 브라우저에서 바로 실행해 볼 수 있습니다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;&gt;&lt;a href=&quot;https://happycgi.com/program/detail.php?number=18145&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qSS3t/dJMb99NbO5I/txcB9IGynsjF5DRhk1iQuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqSS3t%2FdJMb99NbO5I%2FtxcB9IGynsjF5DRhk1iQuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;40&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹프로그램밍 자료실/JAVA 자료</category>
      <category>CGIMALL</category>
      <category>happycgi</category>
      <category>다크모드</category>
      <category>해피CGI</category>
      <author>해피CGI윤실장</author>
      <guid isPermaLink="true">https://happycgi.tistory.com/4037</guid>
      <comments>https://happycgi.tistory.com/4037#entry4037comment</comments>
      <pubDate>Thu, 21 May 2026 09:07:13 +0900</pubDate>
    </item>
    <item>
      <title>[해피CGI][cgimall] 개발자를 위한 5,600개 이상의 브랜드 SVG 아이콘 &amp;ndash; theSVG</title>
      <link>https://happycgi.tistory.com/4036</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;■ 라이센스&lt;br /&gt;&lt;br /&gt;MIT 라이센스&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;■ 상세 설명&lt;br /&gt;&lt;b&gt;&lt;br /&gt;theSVG&lt;/b&gt;는 GitHub, Figma, Google 등 다양한 브랜드 아이콘을 SVG 형태로 제공하는 아이콘 라이브러리이다.&lt;/p&gt;
&lt;div&gt;해당 라이브러리는 npm 패키지 형태로도 제공되지만, 일반 웹사이트에서는 별도의 설치 없이도 쉽게 사용할 수 있다는 것이 가장 큰 장점이다.&lt;br /&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p7HTE/dJMcacwqx4C/z716kjvYaAhWHwj8lIq1p1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p7HTE/dJMcacwqx4C/z716kjvYaAhWHwj8lIq1p1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p7HTE/dJMcacwqx4C/z716kjvYaAhWHwj8lIq1p1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp7HTE%2FdJMcacwqx4C%2Fz716kjvYaAhWHwj8lIq1p1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;357&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;기본적으로 다음과 같은 방식으로 활용할 수 있다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;CDN 방식&lt;br /&gt;이미지 URL을 통해 아이콘을 바로 불러와 사용할 수 있다.&lt;br /&gt;HTML에 &amp;lt;img&amp;gt; 태그만으로 적용 가능하여 가장 간편하다.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;SVG 직접 삽입&lt;br /&gt;사이트에서 SVG 코드를 복사하여 HTML에 직접 삽입하는 방식이다.&lt;br /&gt;외부 의존성이 없어 안정적이며 디자인 커스터마이징에 유리하다.&lt;br /&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;npm 패키지 방식&lt;br /&gt;React, Vue 등 프론트엔드 프로젝트에서 import 형태로 사용할 경우 npm 설치가 필요하다.&lt;br /&gt;하지만 일반적인 웹사이트(PHP, HTML 기반)에서는 사용하지 않아도 된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;즉, 단순 아이콘 사용 목적이라면 npm 설치 없이도 충분히 활용 가능하며,&lt;/div&gt;
&lt;div&gt;개발 환경에 따라 선택적으로 사용하는 구조이다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;■ 활용 포인트&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;브랜드 아이콘 빠른 적용 가능&lt;/div&gt;
&lt;div&gt;SVG 기반으로 고해상도 대응&lt;/div&gt;
&lt;div&gt;색상 및 크기 자유롭게 커스터마이징 가능&lt;/div&gt;
&lt;div&gt;CDN / 직접삽입 / npm 방식 모두 지원&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;■ 추천 사용 환경&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;홈페이지 제작 (기업/쇼핑몰/솔루션 사이트)&lt;/div&gt;
&lt;div&gt;관리자 페이지 UI&lt;/div&gt;
&lt;div&gt;SNS/브랜드 아이콘 표시 영역&lt;/div&gt;
&lt;div&gt;디자인 시안 및 프로토타입 작업&lt;br /&gt;&lt;br /&gt;간단하게 사용하려면 사이트에 접속해서 아이콘 선택 후 HTML 을 이용해도 된다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LqaCl/dJMb990MXDj/Gkim5bS9dFXsa8jk2u27wK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LqaCl/dJMb990MXDj/Gkim5bS9dFXsa8jk2u27wK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LqaCl/dJMb990MXDj/Gkim5bS9dFXsa8jk2u27wK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLqaCl%2FdJMb990MXDj%2FGkim5bS9dFXsa8jk2u27wK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;420&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;&gt;&lt;a href=&quot;https://happycgi.com/program/detail.php?number=18144&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eiq414/dJMcadWlZ1w/8kUllF9DMbxRRIx4Tpodmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feiq414%2FdJMcadWlZ1w%2F8kUllF9DMbxRRIx4Tpodmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;40&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹프로그램밍 자료실/기타 자료</category>
      <category>CGIMALL</category>
      <category>happycgi</category>
      <category>SVG</category>
      <category>아이콘</category>
      <category>해피CGI</category>
      <author>해피CGI윤실장</author>
      <guid isPermaLink="true">https://happycgi.tistory.com/4036</guid>
      <comments>https://happycgi.tistory.com/4036#entry4036comment</comments>
      <pubDate>Wed, 20 May 2026 09:05:17 +0900</pubDate>
    </item>
    <item>
      <title>[해피CGI][cgimall] Elastic hover effect (Chrome only) 탄력적인 마우스 오버 효과</title>
      <link>https://happycgi.tistory.com/4035</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu4asI/dJMcacDbWxI/lkFAcKSVSIvyb6olkGB2Xk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu4asI/dJMcacDbWxI/lkFAcKSVSIvyb6olkGB2Xk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu4asI/dJMcacDbWxI/lkFAcKSVSIvyb6olkGB2Xk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu4asI%2FdJMcacDbWxI%2FlkFAcKSVSIvyb6olkGB2Xk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;232&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;마우스 호버를 하면 탄력적이게 휘어집니다.&lt;br /&gt;마우스 아웃하면 탄력적으로 원래 모양으로 돌아갑니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;1566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FzGMX/dJMcahxIt4F/vspttIxSrEkq9bcgKc7In0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FzGMX/dJMcahxIt4F/vspttIxSrEkq9bcgKc7In0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FzGMX/dJMcahxIt4F/vspttIxSrEkq9bcgKc7In0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFzGMX%2FdJMcahxIt4F%2FvspttIxSrEkq9bcgKc7In0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;1566&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;1566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div&gt;이 소스는 메뉴 텍스트 각 글자가 hover 시 탄성 있게 위로 휘어지는 효과를 만든 CSS입니다.&lt;/div&gt;
&lt;div&gt;핵심은 일반적인 transform 애니메이션이 아니라 아래 내용을 확인하세요.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;@property 로 커스텀 속성 애니메이션 가능하게 만듬&lt;/div&gt;
&lt;div&gt;offset + shape() 로 글자마다 곡선 경로 위에 배치&lt;/div&gt;
&lt;div&gt;sibling-index(), sibling-count() 로 글자 위치별로 다르게 계산&lt;/div&gt;
&lt;div&gt;hover 시 --_s 값을 0 &amp;rarr; 1로 바꿔서 곡률을 키움&lt;br /&gt;&lt;br /&gt;전체 소스는 해당 사이트 또는 첨부 파일을 다운로드 받아서 확인이 가능합니다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;&gt;&lt;a href=&quot;https://happycgi.com/program/detail.php?number=18143&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FNClW/dJMcadWk55a/5qqNhPzDduxQs14wJO2a6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFNClW%2FdJMcadWk55a%2F5qqNhPzDduxQs14wJO2a6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;40&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>CGIMALL</category>
      <category>effect</category>
      <category>happycgi</category>
      <category>hover</category>
      <category>해피CGI</category>
      <author>해피CGI윤실장</author>
      <guid isPermaLink="true">https://happycgi.tistory.com/4035</guid>
      <comments>https://happycgi.tistory.com/4035#entry4035comment</comments>
      <pubDate>Tue, 19 May 2026 09:23:12 +0900</pubDate>
    </item>
    <item>
      <title>[해피CGI][cgimall] Pill Toggle - cpc-true-false 커스텀 토글 버튼</title>
      <link>https://happycgi.tistory.com/4034</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5WsnL/dJMcabRHljP/www6Tkfc234TzytOH1mK00/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5WsnL/dJMcabRHljP/www6Tkfc234TzytOH1mK00/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5WsnL/dJMcabRHljP/www6Tkfc234TzytOH1mK00/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5WsnL%2FdJMcabRHljP%2Fwww6Tkfc234TzytOH1mK00%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;476&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;클릭시 애니메이션 효과가 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;1476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3yDMz/dJMcabRHljN/U2rSXl01koIYL6pyGiafO0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3yDMz/dJMcabRHljN/U2rSXl01koIYL6pyGiafO0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3yDMz/dJMcabRHljN/U2rSXl01koIYL6pyGiafO0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3yDMz%2FdJMcabRHljN%2FU2rSXl01koIYL6pyGiafO0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;1476&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;1476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;핵심내용은 아래와 같습니다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div&gt;.option 컴포넌트 전체 스타일&lt;/div&gt;
&lt;div&gt;내부에 fieldset, legend, 라디오 영역을 배치&lt;/div&gt;
&lt;div&gt;:before 가짜 요소를 이용해서 선택된 버튼 뒤에 움직이는 배경판을 만듬&lt;/div&gt;
&lt;div&gt;:has([value=&quot;no&quot;]:checked) 조건으로 &quot;no&quot;가 체크되면 배경판이 오른쪽으로 이동&lt;/div&gt;
&lt;div&gt;라디오 input은 실제로는 투명하게 덮어두고, 보이는 건 레이블/박스 스타일&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;&gt;&lt;a href=&quot;https://happycgi.com/program/detail.php?number=18142&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6pWGA/dJMcaaFk3Kh/OrNi76V704Q8bvkAKalTw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6pWGA%2FdJMcaaFk3Kh%2FOrNi76V704Q8bvkAKalTw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;40&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>웹프로그램밍 자료실/기타 자료</category>
      <category>CGIMALL</category>
      <category>happycgi</category>
      <category>toggle</category>
      <category>토글</category>
      <category>해피CGI</category>
      <author>해피CGI윤실장</author>
      <guid isPermaLink="true">https://happycgi.tistory.com/4034</guid>
      <comments>https://happycgi.tistory.com/4034#entry4034comment</comments>
      <pubDate>Fri, 15 May 2026 09:10:27 +0900</pubDate>
    </item>
    <item>
      <title>[해피CGI][cgimall] 3D 이미지 큐브 갤러리 인터랙션 효과</title>
      <link>https://happycgi.tistory.com/4033</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;여러 개의 이미지를 3D 큐브 형태로 배치하여 마우스 움직임에 따라 입체적으로 반응하는&lt;br /&gt;이미지 갤러리 효과를 구현한 예제입니다.&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;사용자가 마우스를 움직이면 이미지가 자연스럽게 이동하거나 강조되어 보여지며, &lt;br /&gt;일반적인 이미지 목록보다 더 생동감 있는 화면 구성을 만들 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;포트폴리오 페이지, 작품 갤러리, 상품 소개 페이지 등에서&lt;br /&gt;시각적으로 흥미로운 콘텐츠 표현을 위해 활용할 수 있는 인터랙션 효과입니다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;441&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9xxIW/dJMcafNpkr0/1HzycEaZ78aFnQx3dWWu6K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9xxIW/dJMcafNpkr0/1HzycEaZ78aFnQx3dWWu6K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9xxIW/dJMcafNpkr0/1HzycEaZ78aFnQx3dWWu6K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/9xxIW/dJMcafNpkr0/1HzycEaZ78aFnQx3dWWu6K/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;716&quot; height=&quot;441&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;441&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #0032b2;&quot; data-ke-size=&quot;size26&quot;&gt;HTML 구조&lt;/h2&gt;
&lt;div style=&quot;background-color: #f5f2f0;&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;lt;!-- Lightbox HTML --&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;lt;div id=&quot;lightbox&quot;&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;lt;div id=&quot;close-btn&quot;&amp;gt;&amp;amp;times;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;lt;img id=&quot;lightbox-img&quot; src=&quot;&quot; alt=&quot;Fullsize&quot;&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;lt;script type=&quot;importmap&quot;&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &quot;imports&quot;: {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;three&quot;: &quot;&lt;a href=&quot;https://unpkg.com/three@0.160.0/build/three.module.js&quot;&gt;https://unpkg.com/three@0.160.0/build/three.module.js&lt;/a&gt;&quot;,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;three/addons/&quot;: &quot;&lt;a href=&quot;https://unpkg.com/three@0.160.0/examples/jsm/&quot;&gt;https://unpkg.com/three@0.160.0/examples/jsm/&lt;/a&gt;&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #d22800;&quot; data-ke-size=&quot;size26&quot;&gt;CSS 소스&lt;/h2&gt;
&lt;div style=&quot;background-color: #f5f2f0;&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; body { margin: 0; overflow: hidden; background-color: #000; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; canvas { display: block; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /* Lightbox styles */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #lightbox {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; display: none; /* Hidden by default */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; position: fixed;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; z-index: 1000;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top: 0;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; left: 0;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; width: 100%;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; height: 100%;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; background-color: rgba(0, 0, 0, 0.9);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; justify-content: center;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; align-items: center;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; opacity: 0;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; transition: opacity 0.3s ease;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #lightbox.active {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; display: flex; /* Use flex to center content */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; opacity: 1;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #lightbox img {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; max-width: 90%;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; max-height: 90%;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #close-btn {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; position: absolute;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top: 20px;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; right: 40px;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; color: #fff;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-size: 30px;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cursor: pointer;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-family: sans-serif;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; user-select: none;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #0e7800;&quot; data-ke-size=&quot;size26&quot;&gt;JS 소스&lt;/h2&gt;
&lt;div style=&quot;background-color: #f5f2f0;&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;import * as THREE from 'three';&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; import { OrbitControls } from 'three/addons/controls/OrbitControls.js';&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // === 1. SCENE SETUP ===&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const scene = new THREE.Scene();&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; scene.background = new THREE.Color(0x000000);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; camera.position.set(0, 100, 120);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const renderer = new THREE.WebGLRenderer({ antialias: true });&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; renderer.setSize(window.innerWidth, window.innerHeight);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; renderer.setPixelRatio(window.devicePixelRatio); // For high DPI screens&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; document.body.appendChild(renderer.domElement);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const controls = new OrbitControls(camera, renderer.domElement);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; controls.enableDamping = true; // Inertia&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; controls.dampingFactor = 0.05;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; controls.minDistance = 50;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; controls.maxDistance = 500;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; controls.maxPolarAngle = Math.PI / 2; // Prevent camera from going under the grid&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // Lighting&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const ambientLight = new THREE.AmbientLight(0xffffff, 1.0);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; scene.add(ambientLight);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const dirLight = new THREE.DirectionalLight(0xffffff, 1.5);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; dirLight.position.set(20, 80, 50);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; scene.add(dirLight);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // === 2. CUBE GENERATION ===&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const cols = 12;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const rows = 8;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const cubeSize = 10;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const gap = 0.5;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const step = cubeSize + gap;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // Calculate grid dimensions to center it&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const gridWidth = cols * step - gap;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const gridDepth = rows * step - gap;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const startX = -gridWidth / 2 + cubeSize / 2;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const startZ = -gridDepth / 2 + cubeSize / 2;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const cubes = [];&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const textureLoader = new THREE.TextureLoader();&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const sideMaterial = new THREE.MeshLambertMaterial({ color: 0x222222 }); // Dark grey sides&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const geometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; for (let i = 0; i &amp;lt; cols; i++) {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for (let j = 0; j &amp;lt; rows; j++) {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Use 'seed' to get the same image for thumbnail and full size&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const seed = `img_${i}_${j}`;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const thumbUrl = `&lt;a href=&quot;https://picsum.photos/seed/$&quot;&gt;https://picsum.photos/seed/$&lt;/a&gt;{seed}/200/200`; // Low res for texture&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const fullUrl = `&lt;a href=&quot;https://picsum.photos/seed/$&quot;&gt;https://picsum.photos/seed/$&lt;/a&gt;{seed}/1200/800`; // High res for lightbox&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const texture = textureLoader.load(thumbUrl);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; texture.anisotropy = renderer.capabilities.getMaxAnisotropy();&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const topMaterial = new THREE.MeshLambertMaterial({ map: texture });&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Material array: [Right, Left, Top, Bottom, Front, Back]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const materials = [&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sideMaterial, sideMaterial,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; topMaterial, // Top face has the image&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sideMaterial, sideMaterial, sideMaterial&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ];&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const cube = new THREE.Mesh(geometry, materials);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Positioning&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cube.position.x = startX + i * step;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cube.position.z = startZ + j * step;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cube.position.y = 0;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Store custom data for animation and lightbox&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cube.userData = {&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; targetY: 0,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fullUrl: fullUrl&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; };&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; scene.add(cube);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cubes.push(cube);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // === 3. RAYCASTING SETUP ===&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const raycaster = new THREE.Raycaster();&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const mouse = new THREE.Vector2(9999, 9999); // Start off-screen&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; let hoveredCube = null;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; function onMouseMove(event) {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Normalize mouse coordinates (-1 to +1)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mouse.x = (event.clientX / window.innerWidth) * 2 - 1;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; window.addEventListener('mousemove', onMouseMove, false);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // === 4. CLICK HANDLERS &amp;amp; LIGHTBOX ===&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const mouseDownPos = new THREE.Vector2();&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // Track mouse down position&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; window.addEventListener('mousedown', (event) =&amp;gt; {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mouseDownPos.x = event.clientX;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; mouseDownPos.y = event.clientY;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const lightbox = document.getElementById('lightbox');&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const lightboxImg = document.getElementById('lightbox-img');&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; const closeBtn = document.getElementById('close-btn');&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; function openLightbox(url) {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; lightboxImg.src = url;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; lightbox.style.display = 'flex';&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; requestAnimationFrame(() =&amp;gt; lightbox.classList.add('active'));&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; function closeLightbox() {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; lightbox.classList.remove('active');&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; setTimeout(() =&amp;gt; {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; lightbox.style.display = 'none';&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; lightboxImg.src = &quot;&quot;;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }, 300); // Wait for transition&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; window.addEventListener('click', (event) =&amp;gt; {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Calculate distance between mousedown and mouseup&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const dx = event.clientX - mouseDownPos.x;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const dy = event.clientY - mouseDownPos.y;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const distance = Math.sqrt(dx * dx + dy * dy);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // If moved less than 5px, treat as a click. Otherwise, it's a drag (camera control).&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (distance &amp;lt; 5 &amp;amp;&amp;amp; hoveredCube) {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; openLightbox(hoveredCube.userData.fullUrl);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // Close on background click or close button&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; lightbox.addEventListener('click', (e) =&amp;gt; {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (e.target !== lightboxImg) closeLightbox();&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; closeBtn.addEventListener('click', closeLightbox);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // === 5. ANIMATION LOOP ===&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; function animate() {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; requestAnimationFrame(animate);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; controls.update();&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // LOGIC: Check intersection with actual cubes&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; raycaster.setFromCamera(mouse, camera);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; const intersects = raycaster.intersectObjects(cubes);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (intersects.length &amp;gt; 0) {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Get the first (closest) cube&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; hoveredCube = intersects[0].object;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; } else {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; hoveredCube = null;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Update cursor style&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (hoveredCube) {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; document.body.style.cursor = 'pointer';&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; } else {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; document.body.style.cursor = 'default';&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Animate cubes&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cubes.forEach(cube =&amp;gt; {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (cube === hoveredCube) {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Lift height set to 5&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cube.userData.targetY = 5;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; } else {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cube.userData.targetY = 0;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Smooth interpolation (Lerp)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cube.position.y += (cube.userData.targetY - cube.position.y) * 0.15;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; renderer.render(scene, camera);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; // Handle window resize&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; window.addEventListener('resize', () =&amp;gt; {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; camera.aspect = window.innerWidth / window.innerHeight;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; camera.updateProjectionMatrix();&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; renderer.setSize(window.innerWidth, window.innerHeight);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; });&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp; &amp;nbsp; animate();&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;&gt;&lt;a href=&quot;https://happycgi.com/program/detail.php?number=18141&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4lVhW/dJMcab5gg80/LEbnShb8Q27pqKkkbkehy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4lVhW%2FdJMcab5gg80%2FLEbnShb8Q27pqKkkbkehy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;40&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>웹프로그램밍 자료실/기타 자료</category>
      <category>CGIMALL</category>
      <category>happycgi</category>
      <category>갤러리</category>
      <category>인터랙션</category>
      <category>해피CGI</category>
      <author>해피CGI윤실장</author>
      <guid isPermaLink="true">https://happycgi.tistory.com/4033</guid>
      <comments>https://happycgi.tistory.com/4033#entry4033comment</comments>
      <pubDate>Thu, 14 May 2026 09:05:25 +0900</pubDate>
    </item>
    <item>
      <title>[해피CGI][cgimall] CSS만으로 구현한 인터랙티브 3D 카드 효과</title>
      <link>https://happycgi.tistory.com/4032</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;마우스 움직임에 따라 카드가 자연스럽게 기울어지며&lt;br /&gt;입체적으로 반응하는 3D 인터랙션 효과를 CSS로 구현한 예제입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;transform, perspective, hover 속성을 활용하여&lt;br /&gt;별도의 JavaScript 없이도 카드가 실제로 움직이는 듯한 동적인 UI 효과를 표현할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;카드형 레이아웃, 서비스 소개 영역, 상품 리스트 등 다양한 UI 디자인에 적용하기 좋으며, &lt;br /&gt;간단한 코드만으로도 세련된 인터랙션을 구현할 수 있는 효과적인 예제입니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CliAh/dJMcacpy8Wv/pgNsZ8Vdnn0ugFyxCQGcW0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CliAh/dJMcacpy8Wv/pgNsZ8Vdnn0ugFyxCQGcW0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CliAh/dJMcacpy8Wv/pgNsZ8Vdnn0ugFyxCQGcW0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCliAh%2FdJMcacpy8Wv%2FpgNsZ8Vdnn0ugFyxCQGcW0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;247&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;247&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #0032b2;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;&lt;br /&gt;HTML 구조&lt;/h2&gt;
&lt;div style=&quot;background-color: #f5f2f0;&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- const DATA = [&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1540968221243-29f5d70540bf',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1596135187959-562c650d98bc',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1628944682084-831f35256163',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1590013330451-3946e83e0392',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1590421959604-741d0eec0a2e',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1572613000712-eadc57acbecd',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1570097192570-4b49a6736f9f',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1620789550663-2b10e0080354',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1617775623669-20bff4ffaa5c',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1548600916-dc8492f8e845',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1573824969595-a76d4365a2e6',&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- &lt;span&gt; &lt;/span&gt;'1633936929709-59991b5fdd72'&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- ];&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;- const N = DATA.length;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;.scene&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;.a3d(style=`--n: ${N}`)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;- for(let i = 0; i &amp;lt; N; i++)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;img.card(src=`&lt;a href=&quot;https://images.unsplash.com/photo-$&quot;&gt;https://images.unsplash.com/photo-$&lt;/a&gt;{DATA[i]}?w=280`&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;style=`--i: ${i}` alt='jellyfish')&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;//- generates this kind of HTML:&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;//- &amp;lt;div class=&quot;scene&quot;&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;//-&lt;span&gt; &lt;/span&gt;&amp;lt;div class=&quot;a3d&quot; style=&quot;--n: 12&quot;&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;//- &lt;span&gt; &lt;/span&gt;&amp;lt;img class=&quot;card&quot; src=&quot;image0.jpg&quot; style=&quot;--i: 0&quot; alt=&quot;image description&quot;/&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;//- &lt;span&gt; &lt;/span&gt;&amp;lt;img class=&quot;card&quot; src=&quot;image1.jpg&quot; style=&quot;--i: 1&quot; alt=&quot;image description&quot;/&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;//- &lt;span&gt; &lt;/span&gt;&amp;lt;!-- the rest of the cards --&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;//- &lt;span&gt; &lt;/span&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;//- &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #d22800;&quot; data-ke-size=&quot;size26&quot;&gt;CSS 소스&lt;/h2&gt;
&lt;div style=&quot;background-color: #f5f2f0;&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;/* ====== Relevant CSS ====== */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;.scene, .a3d { display: grid }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;.scene {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;/* prevent scrollbars */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;overflow: hidden;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;/* for 3D look; smaller = more extreme effect */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;perspective: 35em;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;mask: /* lateral fade */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;linear-gradient(90deg,&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;#0000, red 20% 80%, #0000)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;.a3d {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;place-self: center /* middle align */;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;/* don't flatten 3D transformed children&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt; * of this parent having its own 3D transform */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;transform-style: preserve-3d;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;animation: ry 32s linear infinite&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;/* simplest y axis rotation */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;@keyframes ry { to { rotate: y 1turn } }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;.card {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;/* base card width, you may change this */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;--w: 17.5em;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;/* compute base angle corresponding to a card */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;--ba: 1turn/var(--n); /* in the future: sibling-count() */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;grid-area: 1/ 1 /* stack in same one grid cell */;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;width: var(--w);&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;aspect-ratio: 7/ 10;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;object-fit: cover;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;border-radius: 1.5em;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;/* don't want to see back of cards in front of screen plane */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;backface-visibility: hidden;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;/* need to use a transform chain here, cannot use separate&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt; * rotate &amp;amp; translate properties because they'd be applied&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt; * in wrong order (translate, then rotate) for what we need */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;transform:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;/* rotate around y axis; in the future: sibling-index() */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;rotatey(calc(var(--i)*var(--ba)))&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;/* only after that translate along z axis with minus */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;translatez(calc(-1*(.5*var(--w) + .5em)/tan(.5*var(--ba))))&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;@media (prefers-reduced-motion: reduce) {&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;.a3d { animation-duration: 128s }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;/* ====== General page prettifying and layout ====== */&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;html, body { display: grid }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;html { height: 100% }&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span&gt;body { background: #fff3ed }&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2 style=&quot;color: #0e7800;&quot; data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;&gt;&lt;a href=&quot;https://happycgi.com/program/detail.php?number=18140&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oZ0vC/dJMcajoAPZC/1coC6qykEuXeuH3C4o6PAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoZ0vC%2FdJMcajoAPZC%2F1coC6qykEuXeuH3C4o6PAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;40&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>웹프로그램밍 자료실/기타 자료</category>
      <category>CGIMALL</category>
      <category>happycgi</category>
      <category>인터랙션</category>
      <category>카드효과</category>
      <category>해피CGI</category>
      <author>해피CGI윤실장</author>
      <guid isPermaLink="true">https://happycgi.tistory.com/4032</guid>
      <comments>https://happycgi.tistory.com/4032#entry4032comment</comments>
      <pubDate>Wed, 13 May 2026 09:04:30 +0900</pubDate>
    </item>
    <item>
      <title>[해피CGI][cgimall] FilePond &amp;ndash; 드래그앤드롭을 지원하는 JavaScript 파일 업로드 라이브러</title>
      <link>https://happycgi.tistory.com/4031</link>
      <description>&lt;div&gt;FilePond는 웹 애플리케이션에서 파일 업로드 기능을 쉽고 세련되게 구현할 수 있도록 도와주는 JavaScript 라이브러리입니다. &lt;br /&gt;기본 HTML 파일 입력 요소를 확장하여 드래그앤드롭 업로드, 이미지 미리보기, 업로드 진행 상태 표시 등 다양한 사용자 친화적인 기능을 제공합니다.&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;a href=&quot;https://pqina.nl/filepond/&quot;&gt;https://pqina.nl/filepond/&lt;/a&gt;&amp;nbsp;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;464&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K0ar7/dJMcaiXxMrZ/LMIKinRzppzEXNgUzli8n1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K0ar7/dJMcaiXxMrZ/LMIKinRzppzEXNgUzli8n1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K0ar7/dJMcaiXxMrZ/LMIKinRzppzEXNgUzli8n1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK0ar7%2FdJMcaiXxMrZ%2FLMIKinRzppzEXNgUzli8n1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;464&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;464&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;최신버전의 소스는&amp;nbsp;&lt;a href=&quot;https://github.com/pqina/filepond/tree/v5&quot;&gt;https://github.com/pqina/filepond/tree/v5&lt;/a&gt; 에서 다운로드 가능 합니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;사용자는 파일을 업로드 영역에 드래그하여 추가하거나, 파일 선택 창을 통해 업로드할 수 있으며, 업로드되는 파일의 상태를 실시간으로 확인할 수 있습니다. 또한 이미지 업로드 시 썸네일 미리보기를 제공하여 사용자 경험을 크게 향상시킵니다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6857e/dJMcaicfOtD/LfSXcKLDmdrKeO4DaSdy41/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6857e/dJMcaicfOtD/LfSXcKLDmdrKeO4DaSdy41/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6857e/dJMcaicfOtD/LfSXcKLDmdrKeO4DaSdy41/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6857e%2FdJMcaicfOtD%2FLfSXcKLDmdrKeO4DaSdy41%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;362&quot; height=&quot;469&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;469&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;FilePond은 비동기 업로드(AJAX)를 지원하여 파일을 서버로 개별 전송할 수 있으며, 대용량 파일을 안정적으로 업로드할 수 있도록 Chunk Upload(분할 업로드) 기능도 제공합니다. 이를 통해 사용자는 파일 업로드 중에도 다른 작업을 계속 진행할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;또한 다양한 플러그인을 통해 이미지 리사이즈, 이미지 편집, 파일 형식 검증 등 추가 기능을 확장할 수 있으며 React, Vue, Angular, Svelte 등 다양한 프레임워크와도 함께 사용할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;주요 특징&lt;/div&gt;
&lt;div&gt;- 드래그앤드롭 기반 파일 업로드 UI&lt;/div&gt;
&lt;div&gt;- 이미지 미리보기 및 업로드 진행 상태 표시&lt;/div&gt;
&lt;div&gt;- 비동기(AJAX) 파일 업로드 지원&lt;/div&gt;
&lt;div&gt;- 대용량 파일을 위한 Chunk Upload 지원&lt;/div&gt;
&lt;div&gt;- 이미지 리사이즈, 검증 등 다양한 플러그인 확장&lt;/div&gt;
&lt;div&gt;- React, Vue, Angular 등 다양한 프레임워크 지원&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;FilePond을 사용하면 기본적인 파일 업로드 입력창을 보다 직관적이고 편리한 인터페이스로 개선할 수 있으며, &lt;br /&gt;관리자 페이지나 사용자 파일 업로드 기능을 구현할 때 매우 유용하게 활용할 수 있습니다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;&gt;&lt;a href=&quot;https://happycgi.com/program/detail.php?number=18139&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJGqi0/dJMcabEaxFp/fOaW5TbpZiV8cKCLDNRdZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJGqi0%2FdJMcabEaxFp%2FfOaW5TbpZiV8cKCLDNRdZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;40&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>웹프로그램밍 자료실/JAVA 자료</category>
      <category>CGIMALL</category>
      <category>happycgi</category>
      <category>파일업로드</category>
      <category>해피CGI</category>
      <author>해피CGI윤실장</author>
      <guid isPermaLink="true">https://happycgi.tistory.com/4031</guid>
      <comments>https://happycgi.tistory.com/4031#entry4031comment</comments>
      <pubDate>Tue, 12 May 2026 09:07:59 +0900</pubDate>
    </item>
    <item>
      <title>[해피CGI][cgimall] Flatpickr - 가볍고 강력한 날짜, 시간 선택 라이브러리</title>
      <link>https://happycgi.tistory.com/4030</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Flatpickr은 웹사이트에서 날짜와 시간을 선택할 수 있도록 도와주는 JavaScript 기반의 DateTime Picker 라이브러리입니다. &lt;br /&gt;별도의 프레임워크나 라이브러리에 의존하지 않는 구조로 제작되어 가볍고 빠르게 동작하며, &lt;br /&gt;다양한 옵션과 테마를 제공하여 다양한 웹 환경에서 유연하게 사용할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;div&gt;&lt;br /&gt;&lt;a href=&quot;https://flatpickr.js.org/&quot;&gt;https://flatpickr.js.org/&lt;/a&gt;&amp;nbsp;&lt;br /&gt;사이트에서 최신버전의 소스 다운로드 및 데모페이지와 사용방법을 확인 가능 합니다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;575&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vQ6iE/dJMcahElpSy/5vbG9kUqH6brVokl7UIhvK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vQ6iE/dJMcahElpSy/5vbG9kUqH6brVokl7UIhvK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vQ6iE/dJMcahElpSy/5vbG9kUqH6brVokl7UIhvK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvQ6iE%2FdJMcahElpSy%2F5vbG9kUqH6brVokl7UIhvK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;575&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;입력 필드에 간단한 설정만 적용하면 캘린더 UI가 자동으로 생성되며, 사용자는 클릭만으로 날짜와 시간을 편리하게 선택할 수 있습니다. &lt;br /&gt;날짜 선택뿐 아니라 시간 선택, 날짜 범위 선택, 여러 날짜 선택 등의 다양한 기능을 지원합니다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;또한 최소 날짜, 최대 날짜 설정, 특정 날짜 비활성화, 날짜 형식 지정 등 다양한 설정 옵션을 제공하여 &lt;br /&gt;예약 시스템, 일정 관리, 신청폼 등 다양한 웹 서비스에 활용할 수 있습니다.&lt;/div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qGEAb/dJMcagepbgm/ubiYRykk3KZKbnnxQv5ae0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qGEAb/dJMcagepbgm/ubiYRykk3KZKbnnxQv5ae0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qGEAb/dJMcagepbgm/ubiYRykk3KZKbnnxQv5ae0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqGEAb%2FdJMcagepbgm%2FubiYRykk3KZKbnnxQv5ae0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;715&quot; height=&quot;766&quot; data-origin-width=&quot;715&quot; data-origin-height=&quot;766&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;Flatpickr은 React, Vue, Angular 등 다양한 프레임워크와 함께 사용할 수 있으며, &lt;br /&gt;다국어 지원과 여러 테마를 제공해 웹사이트 디자인에 맞게 쉽게 커스터마이징할 수 있습니다.&lt;br /&gt;&lt;br /&gt;다양한 샘플은 아래 페이지를 통해 확인 해보시면 됩니다.&lt;br /&gt;&lt;a href=&quot;https://flatpickr.js.org/examples/&quot;&gt;https://flatpickr.js.org/examples/&lt;/a&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;주요 특징&lt;/div&gt;
&lt;div&gt;&amp;nbsp;- 경량 JavaScript 라이브러리 (의존성 없음)&lt;/div&gt;
&lt;div&gt;&amp;nbsp;- 날짜 선택 / 시간 선택 / 날짜 범위 선택 지원&lt;/div&gt;
&lt;div&gt;&amp;nbsp;- 다양한 옵션 및 이벤트 API 제공&lt;/div&gt;
&lt;div&gt;&amp;nbsp;- 다국어 지원 및 다양한 테마 제공&lt;/div&gt;
&lt;div&gt;&amp;nbsp;- React, Vue, Angular 등 다양한 프레임워크와 호환&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;웹 폼의 날짜 입력 UI를 간편하게 구현하고 싶다면 Flatpickr은 매우 효율적인 선택이 될 수 있습니다.&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;&gt;&lt;a href=&quot;https://happycgi.com/program/detail.php?number=18138&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nHkUm/dJMcabD9KZj/1vT2fjaF4WWieLMrZzN8Q1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnHkUm%2FdJMcabD9KZj%2F1vT2fjaF4WWieLMrZzN8Q1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;40&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹프로그램밍 자료실/JAVA 자료</category>
      <category>CGIMALL</category>
      <category>datetime</category>
      <category>happycgi</category>
      <category>해피CGI</category>
      <author>해피CGI윤실장</author>
      <guid isPermaLink="true">https://happycgi.tistory.com/4030</guid>
      <comments>https://happycgi.tistory.com/4030#entry4030comment</comments>
      <pubDate>Mon, 11 May 2026 10:21:47 +0900</pubDate>
    </item>
    <item>
      <title>[해피CGI][cgimall] Infinite Gallery Hall</title>
      <link>https://happycgi.tistory.com/4029</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;가로로 스크롤 해서 볼 수 있는 갤러리 입니다.&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;미술관 디자인으로 다양한 작품을 볼 수 있습니다.&lt;br /&gt;자세한 내용은 데모를 참고해주시기 바랍니다.&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;HTML&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;200&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;div&gt;&amp;nbsp;&amp;lt;div class=&quot;logo&quot;&amp;gt;GALLERIE D'ART&amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;lt;div id=&quot;canvas-container&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;lt;div id=&quot;ui-layer&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;slide-content&quot; id=&quot;slide-0&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;catalogue-number&quot;&amp;gt;01 / Collection&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;h1&amp;gt;Ethereal &amp;lt;br&amp;gt;Form&amp;lt;/h1&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;description&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Captured in the gentle light of early morning, this piece explores the boundaries between reality and abstraction. The soft textures invite the viewer to look closer, revealing layers of complexity hidden within the simplicity.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;meta-grid&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Artist&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;Elena Varas&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Year&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;2023&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Medium&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;Oil on Linen&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;slide-content&quot; id=&quot;slide-1&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;catalogue-number&quot;&amp;gt;02 / Collection&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;h1&amp;gt;Geometric &amp;lt;br&amp;gt;Silence&amp;lt;/h1&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;description&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; A study in precision and balance. By stripping away organic chaos, the artist reveals the quiet mathematical purity that underlies nature. The composition demands a moment of stillness from its observer.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;meta-grid&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Artist&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;Marcus Thorne&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Year&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;2024&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Medium&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;Acrylic &amp;amp; Graphite&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;slide-content&quot; id=&quot;slide-2&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;catalogue-number&quot;&amp;gt;03 / Collection&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;h1&amp;gt;Fading &amp;lt;br&amp;gt;Horizons&amp;lt;/h1&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;description&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; The horizon line serves as a metaphor for the future&amp;mdash;always visible yet forever out of reach. The bleeding colors suggest the fluidity of memory and the inevitable passage of time.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;meta-grid&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Artist&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;Isabella Rossi&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Year&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;2022&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Medium&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;Watercolor Wash&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;slide-content&quot; id=&quot;slide-3&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;catalogue-number&quot;&amp;gt;04 / Collection&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;h1&amp;gt;The &amp;lt;br&amp;gt;Void&amp;lt;/h1&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;description&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; A minimalist approach challenging the viewer to find meaning in emptiness. The texture of the canvas itself becomes the primary subject, inviting a purely tactile visual experience without distraction.&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;div class=&quot;meta-grid&quot;&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Artist&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;Unknown&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Year&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;Late 20th C.&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;span class=&quot;meta-label&quot;&amp;gt;Medium&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;meta-value&quot;&amp;gt;Mixed Media&amp;lt;/span&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/div&amp;gt;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style=&quot;color: #0000ff;&quot;&gt;&lt;b&gt;CSS&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; width=&quot;200&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;div&gt;
&lt;div&gt;body, html {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; margin: 0;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; padding: 0;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; width: 100%;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; height: 100%;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; overflow: hidden;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; background-color: #f7f7f5;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-family: 'Lato', sans-serif;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; color: #111;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #canvas-container {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; position: fixed;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top: 0;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; left: 0;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; width: 100%;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; height: 100%;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; z-index: 1;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; #ui-layer {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; position: fixed;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top: 0;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; left: 0;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; width: 100%;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; height: 100%;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; z-index: 2;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; pointer-events: none;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .logo {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; position: fixed;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top: 40px;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; left: 50px;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-family: 'Playfair Display', serif;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-weight: 700;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; letter-spacing: 2px;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-size: 1rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text-transform: uppercase;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; z-index: 10;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .slide-content {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; position: absolute;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; top: 25%;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; left: 8%;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; width: 30%;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; max-width: 450px;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; opacity: 0;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; transform: translateY(20px);&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; transition: opacity 0.8s ease, transform 0.8s ease-out;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; pointer-events: auto;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .slide-content.active {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; opacity: 1;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; transform: translateY(0);&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; h1 {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-family: 'Playfair Display', serif;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-weight: 400;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-style: italic;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-size: 4rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; margin: 0 0 1.5rem 0;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; line-height: 1;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; color: #0d0d0d;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .catalogue-number {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-size: 0.7rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text-transform: uppercase;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; letter-spacing: 3px;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; color: #999;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; margin-bottom: 1.5rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; display: inline-block;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; border-bottom: 1px solid #ddd;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; padding-bottom: 5px;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .description {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-size: 1.05rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-weight: 300;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; line-height: 1.8;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; color: #444;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; margin-bottom: 3rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text-align: justify;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .meta-grid {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; display: grid;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; grid-template-columns: 80px 1fr;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; row-gap: 0.8rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; border-top: 1px solid #e0e0e0;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; padding-top: 1.5rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .meta-label {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-size: 0.65rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text-transform: uppercase;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; letter-spacing: 1.5px;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; color: #888;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; align-self: center;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .meta-value {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-family: 'Playfair Display', serif;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-size: 1.1rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-style: italic;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; color: #222;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; .scroll-hint {&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; position: fixed;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bottom: 40px;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; left: 50px;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; font-size: 0.7rem;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text-transform: uppercase;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; letter-spacing: 2px;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; color: #aaa;&lt;/div&gt;
&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;&gt;&lt;a href=&quot;https://happycgi.com/program/detail.php?number=18137&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRP0WX/dJMcabxkCSh/QmigHIxMHgw5DAK8n2EZs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRP0WX%2FdJMcabxkCSh%2FQmigHIxMHgw5DAK8n2EZs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;140&quot; height=&quot;40&quot; data-filename=&quot;0000.png&quot; data-origin-width=&quot;140&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>웹프로그램밍 자료실/기타 자료</category>
      <category>CGIMALL</category>
      <category>Gallery</category>
      <category>happycgi</category>
      <category>갤러리</category>
      <category>해피CGI</category>
      <author>해피CGI윤실장</author>
      <guid isPermaLink="true">https://happycgi.tistory.com/4029</guid>
      <comments>https://happycgi.tistory.com/4029#entry4029comment</comments>
      <pubDate>Fri, 8 May 2026 10:27:26 +0900</pubDate>
    </item>
  </channel>
</rss>