Communitygithub.com

anhlamtruong/living-ui-swarm

React / Next.js 페이지에서 스크롤에 따라 에이전트 커서들이 페이지를 직접 조립하는 애니메이션을 구축합니다.

living-ui-swarm란 무엇인가요?

이 스킬은 React 및 Next.js 애플리케이션에서 '에이전트 스웜(agent swarm)' 빌드 애니메이션을 구현하는 완전한 엔진입니다. 페이지가 스크롤되면, 기획자, 코더, 디자이너, 레이아웃 담당자, 테스터, 리뷰어, 배포자 등 역할별 색상이 지정된 여러 커서들이 마치 실제 팀처럼 협력하여 각 섹션을 조립하는 듯한 효과를 만듭니다. 이들은 카드에 프레임을 그리고, 제목을 타이핑하며 폰트를 교체하고, 이미지를 드래그하고, 목록을 복제하며, 색상환에서 색상을 선택하고, 그리드를 배치한 후 최종 검토 및 승인 과정을 수행합니다. 엔진은 Canvas 오버레이, GSAP, Lenis를 기반으로 하며, 모든 콘텐츠는 실제 서버 렌더링 HTML로 유지되어 SEO와 접근성에 영향을 주지 않습니다. 빌드 과정은 세션 저장소에 기록되어 한 번 본 사용자는 다시 시청하지 않아도 되며, reduced motion 환경, 모바일, 태블릿, 데스크톱 각각에 맞춰 예산과 세부 사항이 최적화되어 있습니다.

지원 대상~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/anhlamtruong/living-ui-skills/tree/HEAD/skills/living-ui-swarm

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

미리보기

스킬 README에서

living-ui-swarm preview

문서

에이전트-스웜 빌드 애니메이션

고정 캔버스 오버레이가 작은 커서 팀을 그려서, 페이지의 각 섹션이 스크롤되어 뷰포트에 들어올 때 해당 섹션을 "빌드"합니다. 콘텐츠는 항상 실제 서버 렌더링 HTML입니다. 엔진은 단지 각 항목을 몇 초 동안 숨기고 각각에 대해 짧은 안무를 실행할 뿐입니다.

라이브 예시: https://www.lamanhtruong.com. 해당 소스는 portfolio-anh@a5d1651에 고정되어 있습니다. assets/swarm/에는 재사용을 위해 정리된 동일한 엔진이 들어 있습니다.

사용 시기와 사용하지 말아야 할 시기

페이지 자체가 홍보 수단인 곳에 사용하세요: 포트폴리오, 런칭 페이지 또는 제품 스토리 페이지, 사례 연구, "우리의 AI에 대해" 페이지. 사람들은 한 번 방문하여 위에서 아래로 스크롤하며, 빌드 과정을 통해 무언가를 알게 됩니다(예: "이 사람은 에이전트와 함께 일을 처리하는구나").

매일 사용하는 모든 것(대시보드, 문서, 설정, 결제, 긴 읽기 자료)에는 건너뛰세요. 빌드는 섹션당 몇 초가 소요되며, 재방문 사용자가 두 번 볼 이유가 없습니다. 이것이 엔진이 완료된 빌드를 탭별로 기억(sessionStorage)하고 새로고침 시 절대 다시 재생하지 않는 이유입니다.

비용:

  • 약 5,000줄의 TypeScript. 빌드 레이어는 next/dynamic을 사용하여 hydration 후에 로드되므로, 초기 페인트는 이를 기다리지 않습니다. 부드러운 스크롤 및 시차 효과(Lenis, GSAP, ScrollTrigger)는 페이지와 함께 로드됩니다. 4개의 living-ui 스킬이 모두 적용된 새로운 Next.js 15 앱의 경로 JS는 약 67 kB(첫 로드 시 170 kB)였습니다.
  • 라이브 로그가 채워지는 동안 약간의 레이아웃 이동 점수(테스트 페이지에서 약 0.03, "좋음" 기준인 0.1보다 훨씬 낮음): 새 로그 라인이 고정 높이 상자 안에서 움직입니다. 페이지의 나머지 부분은 절대 이동하지 않습니다.
  • 하나의 추가 고정 캔버스와 하나의 gsap.ticker 루프. 아무것도 움직이지 않을 때 루프는 멈춥니다.
  • GSAP 3.13+(무료, ScrollTrigger 포함), @gsap/react, Lenis.

한 문단으로 설명하는 작동 방식

<head>의 작은 인라인 스크립트가 첫 번째 페인트 전에 html[data-build="on"]을 설정하고, CSS는 아직 빌드되지 않은 모든 [data-build-item] 요소를 opacity: 0으로 숨깁니다. 투명도(display나 visibility가 아닌)를 사용하면 레이아웃 이동이 없고 접근성 트리에서 제거되는 요소도 없습니다. hydration 후 엔진은 페이지를 [data-build-section]별로 작업 그래프로 스캔하고, 각 섹션에 대해 ScrollTrigger를 장착합니다. 섹션이 뷰포트에 들어오면 계획 → 빌드 → 검토를 실행합니다:

  • 계획: 기획자가 가이드를 그리고 작업을 분배합니다.
  • 빌드: 작업자(worker)들이 거리, 역할 및 중요 경로(critical path)에 따라 작업을 할당받고, 각 작업은 하나의 GSAP 타임라인을 재생합니다.
  • 검토: 리뷰어가 섹션의 윤곽을 그리고 체크리스트를 확인하여 도장을 찍습니다.

완료된 항목과 섹션에는 [data-built]가 설정됩니다. 엔진이 시작되지 않으면 헤드 스크립트가 3초 후에 플래그를 제거하여 아무것도 숨겨지지 않도록 합니다.

아키텍처, 데이터 흐름 및 확장 방법: references/architecture.md.

설치 (Next.js App Router)

  1. 의존성. Next.js 15.1 + React 18.3(작업 예시) 및 Next.js 15.5 + React 19.1 + Tailwind 4(새로운 create-next-app), TypeScript 5.8–5.9, [email protected]–3.15, @gsap/[email protected], [email protected]에서 테스트되었습니다:

    npm i gsap@^3.13 @gsap/react@^2.1 lenis@^1.3
    
  2. 엔진 복사. assets/swarm/을 src/components/swarm/으로 복사하세요. 내부 임포트는 상대 경로이므로 모든 폴더에서 작동합니다.

  3. 플래그 스크립트 추가를 루트 레이아웃 <head>에 추가하세요. React가 hydration되기 전에 <html>에 속성을 설정하므로, <html>에 suppressHydrationWarning을 추가하세요:

    // app/layout.tsx
    import { BuildFlagScript } from "@/components/swarm/build-flag-script";
    
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en" suppressHydrationWarning>
          <head>
            <BuildFlagScript />
          </head>
          <body>{children}</body>
        </html>
      );
    }
    
  4. 엔진을 한 번 마운트하세요. 사용하는 페이지에서 콘텐츠 이후에 마운트합니다:

    // app/page.tsx (서버 컴포넌트; 정적으로 유지됨)
    import "@/components/swarm/swarm.css";
    import { SwarmRoot } from "@/components/swarm/swarm-root";
    
    export default function Page() {
      return (
        <>
          <main>{/* data-build-*로 마크업된 섹션들 */}</main>
          <SwarmRoot />
        </>
      );
    }
    

    SwarmRoot는 또한 gsap.ticker로 구동되는 Lenis 부드러운 스크롤을 마운트합니다. 앱이 이미 Lenis를 실행 중이라면, swarm-root.tsx에서 <SmoothScroll />을 삭제하세요. build-layer.tsx는 useLenis()를 통해 인스턴스를 읽습니다. 이를 위해서는 페이지 어딘가에 root ReactLenis 인스턴스가 있어야 합니다(<ReactLenis root />, smooth-scroll.tsx가 마운트하는 방식). 부모일 필요는 없습니다.

  5. 경로를 정적으로 유지하세요. 엔진은 이미 존재하는 HTML을 장식할 뿐입니다. 세션을 읽기 위해 페이지의 레이아웃 트리에서 cookies(), headers() 또는 auth()를 호출하지 마세요. 대신 클라이언트에서 가져오면 페이지가 사전 렌더링되어 CDN에서 제공될 수 있습니다. SwarmRoot를 마운트하지 않는 페이지에서 플래그 스크립트는 무해합니다. 어떤 요소도 data-build-item을 가지고 있지 않으며, 안전 장치가 플래그를 제거합니다.

  6. 테마 설정. CSS 변수(--swarm-font-mono, --swarm-accent, --swarm-text, --swarm-muted, --swarm-dim, --swarm-line, --swarm-neutral-text|fill|stroke)를 설정한 다음, palette.ts에서 COLORS와 ROLE_COLOR를 편집하세요. 캔버스는 매 프레임마다 CSS 변수를 저렴하게 읽을 수 없으므로 팔레트는 TypeScript에 있습니다.

페이지 마크업

모든 것은 서버 렌더링 마크업의 데이터 속성에 의해 구동됩니다. 전체 참조(기본값 및 휴대폰 동작 포함)는 references/dom-contract.md에 있습니다. 히어로, 카드 그리드, 목록, 이미지 또는 비디오, 지표 스트립 및 피날레를 위한 복사-붙여넣기 레시피는 references/recipes.md에 있습니다.

속성대상의미
data-build-section="name"섹션 래퍼자체 계획, 팀 및 검토가 있는 하나의 빌드 단위
data-build-start="load"섹션스크롤 대신 부트 직후에 빌드, 더 큰 히어로 예산 사용
data-build-item + data-build="frame|type|reveal|image|dup"요소해당 종류의 하나의 작업
data-build-list목록 컨테이너첫 번째 항목이 마스터(frame); dup 형제는 여기서 복사됨
data-build-grid컨테이너 또는 섹션레이아웃 에이전트가 먼저 그리드를 그리고 각 셀을 상자로 표시
data-build-font / -weight / -scaletype 항목모노 초안 후 글꼴 교체; 가중치 스윕; 작게 시작한 후 크기 조정 및 이동
data-build-color="text|fill|stroke"모든 항목색상환이 항목이 나타난 후 실제 색상을 선택
data-build-tuneframe반경 및 패딩 검사기 열기
data-build-from="left|right|top|bottom"image이미지가 드래그되는 가장자리
data-build-keep / ="mobile"모든 항목절대 숨기지 않음(모바일에서는 mobile로), 여전히 주석 처리됨
data-build-label, -order, -role모든 항목태그 텍스트, 순서 힌트, 담당 역할
data-swarm-spawn="role"SVG <g> (히어로 내부)부트 시 팀이 튀어나오는 메시 노드 (≥1024 px)
data-swarm-log컨테이너서버 렌더링 폴백 라인이 라이브 로그로 교체됨
data-build-finale + data-deploy-target + data-deploy-text마지막 섹션, 해당 CTA배포자가 마지막 검토 후 페이지 "배송"
?build=off / ?build=forceURL이 보기에서 비활성화 / 이 탭이 이미 봤더라도 다시 재생

엔진이 설정하는 속성: 활성 중일 때 html[data-build="on"], 완료된 항목 및 섹션에 [data-built], 부트 중 메시 노드에 [data-lit], 피날레 대상에 [data-deployed].

작동을 유지하는 규칙:

  • 섹션 크기. 섹션당 약 한 뷰포트 분량의 항목. 긴 페이지를 분할하세요: 타임라인의 각 작업마다 하나의 섹션, 큰 그리드의 각 카드마다 하나, 섹션 헤더에 대해 별도의 섹션. 작은 섹션은 예산 내에서 완료되어 의도적으로 보입니다. 큰 섹션은 따라잡기 속도로 대체됩니다.
  • 중첩. 다른 항목 내부에 중첩된 항목은 부모를 기다립니다. 카드를 frame으로 표시하고 그 안에 제목, 텍스트 및 칩을 표시하세요.
  • 유형. 짧은 텍스트(약 120자 미만)에만 type을 사용하세요. 더 긴 텍스트는 자동으로 reveal로 대체됩니다. 타이핑은 글리프 상자를 한 번 읽으므로, hydration 후에 변경되는 React 관리 텍스트(카운터, 조회수)는 type이 아닌 frame이어야 합니다.
  • LCP. data-build-keep="mobile"로 휴대폰에서 메인 제목을 계속 표시하세요. 휴대폰에서 히어로 제목은 Largest Contentful Paint이며, 숨기면 Core Web Vitals에 손해입니다. 데스크톱은 여전히 전체 글꼴 교체 빌드를 얻습니다.
  • 라이브 로그. data-swarm-log 컨테이너는 실제 폴백 라인과 고정 높이(.swarm-log)로 서버 렌더링되어야 합니다. 엔진은 이를 라이브 라인으로 교체하고 건너뛰기 또는 마운트 해제 시 다시 복원합니다.

최소 히어로:

<section data-build-section="hero" data-build-start="load">
  <p data-build-item data-build="type">your eyebrow line</p>
  <h1
    data-build-item data-build="type" data-build-font data-build-weight
    data-build-keep="mobile" data-build-order="-1" data-build-role="coder"
  >
    Your Name <span className="serif">(Accent)</span>
  </h1>
  <div data-build-item data-build="image" data-build-from="right" className="avatar">
    <img src="/me.jpg" alt="Portrait" />
  </div>
  {/* next/image도 작동합니다 (엔진이 읽는 내부 <img>를 렌더링함). */}
  <div data-build-list className="buttons">
    <a data-build-item data-build="frame" href="#">GitHub</a>
    <a data-build-item data-build="dup" href="#">LinkedIn</a>
    <a data-build-item data-build="dup" data-build-color="fill" href="#">Resume</a>
  </div>
</section>

부드러움 규칙

이 규칙들은 실제 빌드를 관찰한 결과 나왔습니다. 각각은 눈에 띄는 문제를 해결했습니다.

  • 마감일에 강제로 완료하지 않음. 초기 버전은 실행 중인 모든 타임라인을 5배 속도로 점프시키고 각 섹션의 마감일에 할당되지 않은 항목을 갑자기 나타나게 했습니다. 사람들은 이를 각 섹션 끝의 끊김 현상으로 보았습니다. 예산은 이제 계획 목표입니다.
    • estimate()는 계획에 맞게 작업 지속 시간을 조정합니다.
    • 섹션이 지연될 때 pace()는 따라잡기 요소를 0.7초에 걸쳐 최대 1.7배까지 부드럽게 증가시킵니다.
    • 모든 트윈은 완전한 따라잡기에서도 최소 120ms 동안 지속되며, 타이핑 단계는 최소 16ms 동안 유지됩니다. 아무것도 갑자기 나타나지 않습니다.
    • 더 빠르게 해야 한다면(palette.ts의 speed) 커서를 더 빠르게 만드세요. 애니메이션을 자르지 마세요.
  • 불투명도, clip-path, filter 및 transform만 애니메이션 처리. 레이아웃 쓰기 없음. 글꼴 교체 초안은 흐름 외부 레이어의 클론이며, 스케일 작업은 끝날 때 인라인 변환을 제거합니다.
  • 하나의 시계. 시뮬레이션과 캔버스 그리기는 단일 gsap.ticker 콜백에서 실행됩니다. React 상태는 빌드가 시작되거나 중지될 때만 변경되며, 프레임당 변경되지 않습니다. 화면에 에이전트가 없으면 티커가 중지됩니다.
  • 미리 측정. 접힌 부분 아래 약 2뷰포트까지의 섹션은 requestIdleCallback 조각으로 측정됩니다. 섹션 시작은 스크롤 프레임에서 큰 레이아웃 읽기 버스트를 절대 발생시키지 않습니다.
  • 읽는 사람 존중.
    • 빌드되지 않은 섹션 내부의 요소에 포커스가 가면 해당 섹션을 즉시 완료하므로 키보드 사용자는 기다릴 필요가 없습니다.
    • 빌드 중 크기 조정은 실행 중인 섹션을 완료합니다. 측정된 rect가 오래되었기 때문입니다.
    • 숨겨진 탭은 모든 것을 일시 중지합니다.
    • HUD는 항상 건너뛰기, 다시 재생 및 검사를 제공합니다.
  • 휴대폰은 더 적게 받음. 각 프로필에는 자체 예산, 팀 및 세부 사항이 있습니다(palette.ts의 detectProfile()).
    • 휴대폰: 8명의 에이전트, 섹션당 약 2.2초, 페이지당 하나의 글꼴 교체, 섹션당 하나의 색상 선택, 목록당 하나의 dup 스윕, 검사기 패널 없음, 스케일 작업 없음, 배포자 없음.
    • 태블릿: 11명의 에이전트.
    • 데스크톱: 15명의 에이전트, 섹션당 약 3.9초, 히어로의 경우 5.25초.

"더 빠르게", "더 차분하게" 및 "더 많은 에이전트"를 위한 조정: references/tuning.md.

움직임 줄이기

엔진은 OS에서 움직임 줄이기를 요청한 사람을 포함한 모든 방문자에게 재생됩니다. 사이트 소유자는 빌드가 페이지의 핵심이라고 결정했으며, HUD는 항상 건너뛰기를 제공하므로 이는 맹목적으로 복사할 기본값이 아닌 의도적인 선택입니다. 대신 설정을 존중하려면 다음 세 가지 변경을 모두 수행하세요:

// build-flag-script.tsx: CODE 내부, d.setAttribute('data-build','on') 전에
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;

// motion.ts
export function offByUrl(): boolean {
  return OFF_RE.test(window.location.search) ||
    window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}

// smooth-scroll.tsx
respectReducedMotion: true,

두 가지 방법을 모두 테스트하세요: 일부 브라우저(및 일부 자동화 설정)는 아무도 선택하지 않았는데도 reduce를 보고합니다. 이것이 여기서 기본값이 "켜짐"인 이유 중 하나입니다.

다른 효과와 함께 작업하기

  • [data-built] 게이트. 엔진은 요소 rect를 읽고 빌드하는 동안 불투명도와 clip-path를 전환합니다. 틸트, 글리치, 스크램블, 콘텐츠의 시차 효과 또는 항목을 변환하는 다른 효과는 일찍 실행되면 해당 측정을 손상시킵니다. 항목과 해당 섹션이 [data-built]를 가질 때 또는 html[data-build]가 "on"이 아닐 때만 이러한 효과를 시작하세요. 준비된 도우미:

    export function isBuilt(el: Element): boolean {
      if (document.documentElement.getAttribute("data-build") !== "on") return true;
      const item = el.closest("[data-build-item]");
      if (item && !item.hasAttribute("data-built")) return false;
      const section = el.closest("[data-build-section]");
      return !section || section.hasAttribute("data-built");
    }
    
  • 비디오. <video poster>를 감싸는 image 항목은 포스터 프레임을 썸네일로 사용하여 드래그됩니다. 래퍼에 data-build-label(예: clip.mp4)을 제공하세요. 파일 이름을 읽을 <img>가 없기 때문입니다.

  • 장식용 시차 효과는 장식만 움직이는 곳이라면 어디든 괜찮습니다. 엔진이 측정하는 항목에는 절대 배치하지 마세요.

직접 만들기

여기 있는 엔진은 독창적인 구현이며, 이 엔진으로 구축하는 모든 것에도 동일한 기준이 적용됩니다. 사이트에서 영감을 받았다면, 그 사이트를 식별 가능하게 만드는 요소를 기록하고 정확히 그 요소들을 피하세요. 빌려오는 것은 괜찮지만, 복사해서 붙여넣는 것은 안 됩니다. 자신의 디자인 언어, 자신의 타이밍, 자신의 팀 구성을 만드세요.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

관련 스킬