Svelte에서 Melt UI로 접을 수 있는 컴포넌트 만들기
Source: Dev.to
Melt UI Collapsible 가이드
Melt UI는 WAI‑ARIA 가이드라인을 따르는 Svelte용 헤드리스이며 접근 가능한 컴포넌트 빌더를 제공합니다. Collapsible 빌더를 사용하면 기본 제공되는 전체 키보드 내비게이션 및 스크린 리더 지원과 함께 확장/축소 가능한 콘텐츠 섹션을 만들 수 있습니다.
이 가이드는 Melt UI의 createCollapsible 빌더를 사용한 실용적이고 프로덕션 준비가 된 콜래퍼시블 컴포넌트 구현 과정을 단계별로 안내합니다. 추상화가 아닌 실제 사용 패턴에 초점을 맞춥니다.
왜 Svelte와 함께 @melt-ui/svelte (Melt UI)를 사용해야 할까요?
Svelte 애플리케이션은 FAQ, 아코디언, 네비게이션 메뉴, 상세 뷰 등에서 확장 가능한 콘텐츠 섹션이 자주 필요합니다. Melt UI의 Collapsible 빌더는 다음과 같은 이유로 이 생태계에 자연스럽게 맞습니다:
- 완전한 WAI‑ARIA 준수를 통한 접근성 보장
- 헤드리스 디자인 (스타일은 직접 제어)
- 뛰어난 타입 안전성을 제공하는 TypeScript 지원
- 간단하고 선언적인 API
- 내장된 키보드 내비게이션
- Svelte 외에 외부 의존성 없음
빌더 패턴을 사용하면 원하는 어떤 요소에도 collapsible 동작을 연결할 수 있어 마크업과 스타일을 완전히 제어할 수 있습니다.
요구 사항
시작하기 전에, 다음을 준비하세요:
- Svelte 프로젝트 (SvelteKit 또는 독립형 Svelte)
- Node.js 16+ 및 npm/pnpm/yarn
- Svelte 컴포넌트와 스토어에 대한 기본적인 친숙함
설치
Install Melt UI using your preferred package manager:
npm install @melt-ui/svelte
# or
pnpm add @melt-ui/svelte
# or
yarn add @melt-ui/svelte
The package includes all builders and their TypeScript definitions.
구성
추가 설정이 필요하지 않습니다. Melt UI는 Svelte와 바로 사용할 수 있습니다. TypeScript를 사용 중이라면 tsconfig.json에 올바른 모듈 해석이 포함되어 있는지 확인하십시오:
{
"compilerOptions": {
"moduleResolution": "bundler",
"target": "ES2020"
}
}
기본 사용법
가장 간단한 방법은 createCollapsible 빌더를 사용하는 것입니다:
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
const {
elements: { root, content, trigger },
states: { open }
} = createCollapsible();
</script>
<div use:melt={root}>
<button use:melt={trigger}>
{$open ? 'Close' : 'Open'}
</button>
<div use:melt={content}>
This content can be expanded and collapsed.
</div>
</div>
createCollapsible()가 반환하는 것
- elements –
root,content,trigger요소에 대한 Svelte 스토어. - states – 접힌 상태를 추적하는
open같은 반응형 스토어.
melt 액션을 사용하여 빌더의 동작을 요소에 연결합니다. open 스토어는 반응형이며, 접힌 상태가 변경될 때 자동으로 업데이트됩니다.
Source: …
고급 기능
Tailwind CSS로 스타일링
Melt UI는 헤드리스이기 때문에 스타일링을 완전히 제어할 수 있습니다:
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
const {
elements: { root, content, trigger },
states: { open }
} = createCollapsible();
</script>
<div use:melt={root} class="border rounded">
<button use:melt={trigger} class="px-4 py-2 bg-gray-200">
Toggle Content <span class="ml-2">▼</span>
</button>
<div use:melt={content} class="p-4">
This is the collapsible content area.
</div>
</div>
제어된 상태
외부에서 콜랩시블 상태를 제어하려면 writable 스토어를 전달합니다:
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
import { writable } from 'svelte/store';
const isOpen = writable(false);
const {
elements: { root, content, trigger },
states: { open }
} = createCollapsible({ open: isOpen });
function toggle() {
isOpen.update(n => !n);
}
</script>
<div use:melt={root}>
<button use:melt={trigger} on:click={toggle}>
{$open ? 'Close' : 'Open'}
</button>
<div use:melt={content}>
Controlled content
</div>
</div>
아코디언 만들기
여러 콜랩시블 상태를 관리하여 아코디언을 구축합니다:
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
import { writable } from 'svelte/store';
const openIndex = writable(null);
const items = [
{ id: 1, title: 'Item 1', content: 'Content for item 1' },
{ id: 2, title: 'Item 2', content: 'Content for item 2' },
{ id: 3, title: 'Item 3', content: 'Content for item 3' }
];
function createItemCollapsible(index) {
const isOpen = writable(openIndex.get() === index);
const unsubscribe = openIndex.subscribe(value => {
isOpen.set(value === index);
});
return {
collapsible: createCollapsible({ open: isOpen }),
unsubscribe
};
}
</script>
{#each items as item, index}
{@const { collapsible, unsubscribe } = createItemCollapsible(index)}
{@const { elements: { root, content, trigger }, states: { open } } = collapsible}
<div use:melt={root} class="border-b">
<button
use:melt={trigger}
on:click={() => openIndex.set($open ? null : index)}
class="w-full px-4 py-2 text-left"
>
{item.title}
</button>
{#if $open}
<div use:melt={content} class="p-4">
{item.content}
</div>
{/if}
</div>
{/each}
커스텀 애니메이션
Svelte의 전환 지시자를 사용해 애니메이션을 추가합니다:
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
import { slide } from 'svelte/transition';
const {
elements: { root, content, trigger },
states: { open }
} = createCollapsible();
</script>
<div use:melt={root}>
<button use:melt={trigger}>
{$open ? 'Hide' : 'Show'} details
</button>
{#if $open}
<div use:melt={content} transition:slide>
Animated collapsible content.
</div>
{/if}
</div>
결론
Melt UI의 헤드리스이며 접근 가능한 Collapsible 빌더는 Svelte에서 완전히 맞춤화된 ARIA‑준수 확장 섹션을 만들 수 있는 유연성을 제공합니다. 빌더 패턴을 활용함으로써 마크업, 스타일링 및 상태 관리에 대한 완전한 제어를 유지하면서 내장된 접근성 및 키보드 지원의 혜택을 누릴 수 있습니다. 즐거운 코딩 되세요!
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
import { slide } from 'svelte/transition';
const {
elements: { root, content, trigger },
states: { open }
} = createCollapsible();
</script>
<div use:melt={root}>
<button use:melt={trigger}>Toggle</button>
{#if $open}
<div use:melt={content} transition:slide>
This content slides in and out.
</div>
{/if}
</div>
전체 화면 제어
- 전체 화면 모드 진입
- 전체 화면 모드 종료
일반적인 문제 / 트러블슈팅
Collapsible doesn’t toggle
melt액션이 세 요소 모두(root,trigger,content)에 적용되었는지 확인하세요.- 스토어에 접근할 때
$접두사를 사용했는지 확인하세요:$open. - 빌더가 컴포넌트 초기화 시 한 번만 호출되는지 확인하세요(조건부로 호출되지 않도록).
Accessibility attributes missing
melt액션이 요소에 적용되어 있고 조건부 렌더링으로 인해 제거되지 않았는지 확인하세요.- ARIA 속성을 수동으로 추가하지 마세요 – 빌더가 자동으로 처리합니다.
TypeScript errors
- 필요할 때
@melt-ui/svelte에서 타입을 가져오세요. - 최상의 TypeScript 지원을 위해 Svelte **v4+**를 사용하세요.
Content not animating
{#if}조건부 렌더링을 사용할 경우melt액션이 계속 적용되어 있는지 확인하세요.- Svelte 전환(
transition:slide등)은 content 요소에 적용하고, root 요소에는 적용하지 마세요.
프로덕션 모범 사례
- 접근성을 보장하기 위해 모든 빌더 요소에
melt액션을 항상 사용하세요. - 키보드 탐색(
Tab,Enter,Space,Escape)을 테스트하세요. - 스크린 리더로 ARIA 속성을 확인하세요.
- 스타일링은 로직과 분리하세요 – Melt UI는 동작을 처리하고, 여러분은 외관을 담당합니다.
- 더 나은 성능을 위해 JavaScript 애니메이션보다 CSS 전환을 선호하세요.
- 아코디언에서는 UX 향상을 위해 한 번에 하나의 항목만 열 수 있도록 하세요.
- 메모리 누수를 방지하기 위해 컴포넌트가 파괴될 때 구독을 정리하세요.
최종 메모
Melt UI의 Collapsible 빌더는 최소한의 설정으로 확장 가능한 콘텐츠에 대한 견고한 기반을 제공합니다. 헤드리스 접근 방식 덕분에 접근성 준수를 유지하면서 스타일을 완전히 제어할 수 있습니다.
Collapsible이 정상적으로 작동하면 일반적인 다음 단계는 다음과 같습니다:
- 공유 상태를 가진 accordion 컴포넌트 구축.
- 사용자 정의 애니메이션 및 전환 추가.
- 확장 가능한 네비게이션 메뉴를 위한 라우팅 통합.
- Collapsible 섹션이 포함된 상세 뷰 생성.