메뉴 클릭 시 해당 섹션으로 부드럽게 이동하고, 스크롤 위치에 따라 활성 섹션을 자동으로 추적하는 크로스 프레임워크 라이브러리입니다.
- 자동 활성 섹션 추적: IntersectionObserver를 사용한 정확한 섹션 감지
- 크로스 프레임워크: React, Vue 3, Svelte 지원
- 경량: Core 패키지는 zero dependency
- TypeScript: 완전한 타입 정의 제공
- URL 해시 동기화: 활성 섹션을 URL hash에 자동 반영
- 키보드 네비게이션:
Alt+ArrowDown/Alt+ArrowUp지원 - 커스텀 스크롤 컨테이너:
window외의 스크롤 영역 지원 - 스크롤 진행률 추적: 섹션별 0–1 progress 콜백
npm install @jump-section/reactnpm install @jump-section/vuenpm install @jump-section/sveltenpm install @jump-section/coreimport { ScrollSectionProvider, useScrollSection } from '@jump-section/react';
function App() {
return (
<ScrollSectionProvider offset={-80} behavior="smooth" hash keyboard>
<Navigation />
<Content />
</ScrollSectionProvider>
);
}
function Navigation() {
const { scrollTo, activeId } = useScrollSection();
return (
<nav>
<button
onClick={() => scrollTo('section-1')}
className={activeId === 'section-1' ? 'active' : ''}
>
Section 1
</button>
<button
onClick={() => scrollTo('section-2')}
className={activeId === 'section-2' ? 'active' : ''}
>
Section 2
</button>
</nav>
);
}
function Content() {
const { registerRef: ref1 } = useScrollSection('section-1');
const { registerRef: ref2 } = useScrollSection('section-2');
return (
<main>
<section ref={ref1}>
<h2>Section 1</h2>
</section>
<section ref={ref2}>
<h2>Section 2</h2>
</section>
</main>
);
}<script setup lang="ts">
import { ScrollSectionProvider } from '@jump-section/vue';
</script>
<template>
<ScrollSectionProvider :offset="-80" behavior="smooth" hash keyboard>
<Navigation />
<Content />
</ScrollSectionProvider>
</template>Navigation.vue
<script setup lang="ts">
import { useScrollSection } from '@jump-section/vue';
const { scrollTo, activeId } = useScrollSection();
</script>
<template>
<nav>
<button @click="scrollTo('section-1')" :class="{ active: activeId === 'section-1' }">
Section 1
</button>
<button @click="scrollTo('section-2')" :class="{ active: activeId === 'section-2' }">
Section 2
</button>
</nav>
</template>Content.vue
<script setup lang="ts">
import { useScrollSection } from '@jump-section/vue';
const { registerRef: section1Ref } = useScrollSection('section-1');
const { registerRef: section2Ref } = useScrollSection('section-2');
</script>
<template>
<main>
<section :ref="section1Ref"><h2>Section 1</h2></section>
<section :ref="section2Ref"><h2>Section 2</h2></section>
</main>
</template>import { ScrollManager } from '@jump-section/core';
const manager = new ScrollManager({
offset: -80,
behavior: 'smooth',
hash: true,
keyboard: true,
});
manager.registerSection('section-1', document.getElementById('section-1')!);
await manager.scrollTo('section-1');
const unsubscribe = manager.onActiveChange((activeId, meta) => {
console.log('Active:', activeId, '| Previous:', meta.previous, '| Direction:', meta.direction);
});
manager.destroy();| Option | Type | Default | Description |
|---|---|---|---|
offset |
number |
0 |
고정 헤더 등을 위한 수직 오프셋 (px) |
behavior |
ScrollBehavior |
'smooth' |
스크롤 동작: 'smooth' | 'instant' | 'auto' |
hash |
boolean |
false |
활성 섹션을 URL hash에 자동 동기화 |
keyboard |
boolean |
false |
Alt+ArrowDown / Alt+ArrowUp 키보드 네비게이션 활성화 |
root |
HTMLElement | null |
null |
커스텀 스크롤 컨테이너 (window 대신 사용) |
jump-section/
├── packages/
│ ├── core/ # framework-agnostic core (zero dependency)
│ ├── react/ # React hooks + components
│ ├── vue/ # Vue 3 composables + component
│ └── svelte/ # Svelte composables
├── docs/
├── package.json
├── pnpm-workspace.yaml
└── turbo.json
- Node.js >= 18
- pnpm >= 9.0.0
pnpm install
pnpm build
pnpm test
pnpm format| Command | Description |
|---|---|
pnpm build |
전체 패키지 빌드 (turbo) |
pnpm test |
전체 테스트 실행 (vitest) |
pnpm test:coverage |
커버리지 포함 테스트 |
pnpm format |
코드 포맷팅 (prettier) |
pnpm check-exports |
패키지 export 검증 |
이 프로젝트는 n8n + Gemini 기반의 AI 파이프라인이 연동되어 있습니다.
Gemini Code Assist가 PR 리뷰를 제출하면 자동으로 실행됩니다. 리뷰 코멘트와 CI 실패를 분석해 수정 커밋을 올립니다.
PR 코멘트에 아래 커맨드를 입력하면 Gemini가 수정 커밋을 올립니다.
| 커맨드 | 설명 |
|---|---|
/fix-build |
CI 빌드/타입 에러 분석 후 수정 |
/fix-lint |
PR 변경 파일에 Prettier 포맷 적용 |
/fix |
리뷰 코멘트에 답장으로 사용 — 해당 코멘트 내용을 반영해 수정 |
Issue를 열거나 Pull Request를 보내주세요.
ISC © k_jin.0
