Melt UI의 스토어/액션 빌더는 사용자가 소유한 Svelte 마크업에 접근성 동작을 연결합니다. 이 가이드에서는 @melt-ui/svelte 0.86.6을 사용해 속성, 액션, 쓰기 가능한 스토어를 통해 라디오, 슬라이더, 스위치 빌더를 연결합니다. 이는 최신 runes API와는 별개입니다.
Svelte 5.57.1에서 실행되는 @melt-ui/svelte 0.86.6의 store/action 빌더를 검증했습니다. 이는 문서화된 빌더 API이며, 별도의 차세대 runes API가 아닙니다. Melt는 헤드리스 라이브러리입니다. 아래의 모든 --study-* 시각 값은 애플리케이션에서 직접 설정한 값이며 전체 구성을 제공합니다.
이 예제는 @melt-ui/svelte 0.86.6의 store/action API를 사용합니다. createRadioGroup, createSlider, createSwitch는 요소 빌더와 상태 스토어를 반환합니다. 일반 button, span, div 요소에 속성과 액션을 적용하면 네이티브 컨트롤 동작과 연결됩니다.
이 패키지는 녹색 종이 질감, 포스터 타이포그래피 또는 패널 형태를 제공하지 않습니다. 이러한 요소는 완전한 원본 스타일시트에서 직접 정의합니다. 문서에는 별도의 최신 runes API도 소개되어 있지만, 이 페이지에서는 서로 호환되지 않는 두 세대의 예제를 섞지 않고 검증된 빌더 버전을 명시합니다.
요소에는 빌더 속성과 액션이 모두 필요합니다. 이 예제에서는 {...$alignRoot} use:alignRoot, {...$alignItem(value)} use:alignItem와 같은 형식을 사용합니다. 속성은 역할, 상태, 위치 지정을 전달하고, 액션은 인터랙티브 동작과 정리를 설정합니다.
간결한 use:melt 구문을 사용하려면 Melt의 선택적 전처리기가 필요합니다. 전처리기 없이 사용하면 빌더가 조용히 적용되지 않고 구체적인 런타임 오류가 발생합니다. 수정된 코드는 모든 곳에서 명시적 구문을 사용하므로 아래 Vite 설정에서 전처리기 의존성을 누락하지 않습니다. 각 슬라이더 thumb 역시 반환된 속성을 펼치고 thumb.action을 호출합니다.
--study-* 역할은 바깥 캔버스, 컨트롤 표면, 텍스트, 구분선, 액션, 포커스, 포스터 용지, 장식용 강조 색을 구분합니다. 이름이 지정된 여섯 가지 간격은 4픽셀부터 32픽셀까지입니다. 이는 이 구성을 재현 가능하게 하려고 직접 정한 값이며, Melt가 관리하는 토큰 카탈로그가 아닙니다.
선택된 라디오 버튼과 스위치는 data-state=checked를 사용합니다. 슬라이더에서 반환하는 인라인 위치 값은 thumb와 선택된 범위가 나타날 위치를 결정합니다. 스타일시트는 여기에 형태와 색상만 더합니다. 네이티브 위치를 직접 계산한 백분율로 대체하면 로직이 중복되고 방향이나 진행 방향 지원이 깨질 수 있습니다.
createRadioGroup은 왼쪽 정렬을 기본값으로 사용하고, 가로 방향과 순환 탐색을 활성화합니다. 각 라디오 항목에는 left, center, right라는 고정 값이 있습니다. 포스터의 text-align은 선택된 값을 바로 읽으므로 키보드 탐색과 포인터 선택이 같은 시각적 구성에 반영됩니다.
그룹에는 접근 가능한 Text alignment 레이블이 지정됩니다. 세 가지 옵션은 클래스를 수동으로 전환하는 일반 버튼이 아니라 네이티브 빌더가 연결된 라디오 컨트롤입니다. 선택 상태는 시맨틱 정보와 채워진 스타일에 사용하는 data-state 선택자 모두에 반영됩니다.
Center를 선택하고 포스터 정렬을 확인하세요.
Letter spacing 슬라이더에 포커스를 놓고 End 키를 눌러 8px로 설정하세요.
슬라이더는 스칼라 값이 아니라 defaultValue:[2]로 시작합니다. 설치된 0.86.6 빌더는 elements.thumbs와 배열을 반환하는 스토어를 제공하며, 템플릿은 반환된 thumb를 각각 렌더링합니다. 포스터는 $tracking[0]을 읽어 픽셀 단위 자간 값으로 적용합니다.
범위는 0부터 8까지이며 간격은 1입니다. Home과 End 키로 범위 끝에 도달하고, 화살표 키로 지정된 간격만큼 이동할 수 있습니다. thumb가 하나뿐인 이 구성에서는 $tracking[0]을 읽고, thumb를 추가할 때는 별개의 스칼라 상태를 새로 만들지 말고 배열 모델을 유지하세요.
createSwitch는 켜진 상태로 시작하며, 상태에 따라 포스터에 guides 클래스를 적용합니다. 이 클래스는 24픽셀 간격의 은은한 반복 그리드 그라데이션 두 개를 그립니다. 스위치를 끄면 정렬이나 자간은 바뀌지 않고 그라데이션만 제거됩니다.
화면에 표시되는 Show layout grid 레이블은 스위치의 고정 ID와 연결됩니다. thumb 이동에는 CSS 전환 효과가 적용되며, 모션 감소 설정을 위한 재정의도 제공됩니다. 장식용 궤도 일러스트에는 aria-hidden이 적용되어 있고 인터랙션이 없습니다. 실제 컨트롤의 시맨틱 정보와 혼동되지 않습니다.
Melt UI 디자인 시스템: 컨트롤 상태를 중복하지 않고 쓰기 가능한 스토어를 활용합니다
제어 상태를 중복하지 않고 쓰기 가능한 스토어 조정하기
정렬, 자간, 그리드 표시 여부는 빌더의 쓰기 가능한 스토어에 유지됩니다. 렌더링되는 포스터는 해당 스토어를 직접 읽습니다. text-align은 정렬 값을 사용하고, letter-spacing은 슬라이더 값의 첫 항목을 사용하며, CSS 클래스는 스위치의 checked 값을 사용합니다. 같은 세 가지 설정을 별도의 애플리케이션 모델에 중복하면 불필요한 동기화 작업이 생깁니다.
저장된 스냅샷은 해당 값의 시그니처를 바탕으로 생성됩니다. 값이 바뀌면 확인 상태가 해제되고, 초기화하면 스토어에 left, [2], true가 다시 기록됩니다. 배열의 요소가 하나뿐이어도 배열이어야 합니다. 슬라이더의 값 계약과 일치하지 않으므로 썸이 하나만 보여도 스칼라 값 2를 할당하면 안 됩니다.
라디오 그룹에는 ‘텍스트 정렬’ 레이블을 지정하고, 슬라이더 썸은 화면에 표시되는 ‘자간’ 텍스트를 참조하며, 스위치는 화면에 표시되는 레이블과 연결합니다. 이러한 이름은 주변의 장식용 래퍼가 아니라 실제 빌더가 적용된 요소에 지정해야 합니다.
명시적 템플릿 구문을 사용하면 속성과 액션을 함께 적용할 수 있습니다. 슬라이더 썸의 경우 반환된 항목에 액션이 포함되므로, 반복문에서 썸 속성을 펼치고 thumb.action을 호출합니다. 트랙과 범위의 스타일을 지정할 때는 빌더가 제공하는 위치 지정 결과를 그대로 유지하세요. 이 결과는 실제 숫자 상태와 방향을 나타내며, 색상과 기하학적 형태는 스타일시트에서 지정합니다.
Melt UI 디자인 시스템: 일반적인 Svelte 프로젝트에서 검증된 빌더 API 사용하기
일반적인 Svelte 프로젝트에서 검증된 빌더 API 사용하기
완성된 컴포넌트는 $alignment, $tracking, $grid를 사용하는 Svelte 스토어 구독을 활용합니다. Svelte 5에서는 이 기존 컴포넌트 구문을 컴파일할 수 있으며, 진입점에서는 현재 마운트 함수를 사용합니다. 전체 시각적 레이어를 앞에서 제공하므로 사용자 지정 아이콘 라이브러리나 스타일시트 패키지는 필요하지 않습니다.
아래의 표준 Vite Svelte 설정은 @melt-ui/svelte 0.86.6과 Svelte 5.57.1을 고정 버전으로 사용합니다. 컴포넌트에서 이미 이에 상응하는 명시적 구문을 사용하므로 Melt 전처리기는 설치하지 않습니다. 최신 runes 패키지로 이전할 때는 의존성 버전만 바꾸지 말고 API를 의도에 맞게 다시 작성하세요.
createRadioGroup, createSlider, createSwitch는 일반 마크업에 기본 의미와 동작을 적용합니다.
svelte
<script>
import {createRadioGroup,createSlider,createSwitch} from '@melt-ui/svelte';
const {elements:{root:alignRoot,item:alignItem},states:{value:alignment}}=createRadioGroup({defaultValue:'left',orientation:'horizontal',loop:true});
const {elements:{root:trackingRoot,range:trackingRange,thumbs},states:{value:tracking}}=createSlider({defaultValue:[2],min:0,max:8,step:1});
const {elements:{root:gridRoot},states:{checked:grid}}=createSwitch({defaultChecked:true});
let saved='';let previous='';
$: current=`${$alignment}|${$tracking[0]}|${$grid}`;
$: if(current!==previous){saved='';previous=current}
function save(){saved=`Saved locally: ${$alignment} aligned, ${$tracking[0]}px tracking, grid ${$grid?'on':'off'}.`}
function reset(){alignment.set('left');tracking.set([2]);grid.set(true);saved=''}
</script>
<main class="poster-shell"><header><p class="eyebrow">TYPE COMPOSITION · LIVE STUDY</p><h1>A small change. A different rhythm.</h1><p>Use native Melt UI builders to change the alignment, tracking and guide visibility of an original typographic poster.</p></header><div class="poster-layout"><section class="controls"><h2>Composition</h2><p class="control-label" id="alignment-label">Text alignment</p><div {...$alignRoot} use:alignRoot aria-labelledby="alignment-label" class="alignment">{#each ['left','center','right'] as value}<button {...$alignItem(value)} use:alignItem class="align-option">{value[0].toUpperCase()+value.slice(1)}</button>{/each}</div><div class="tracking-control"><p class="control-label" id="tracking-label">Letter spacing <strong>{$tracking[0]}px</strong></p><div {...$trackingRoot} use:trackingRoot class="slider"><div {...$trackingRange} use:trackingRange class="slider-range"></div>{#each $thumbs as thumb}<span {...thumb} use:thumb.action aria-labelledby="tracking-label" class="slider-thumb"></span>{/each}</div><div class="scale"><span>0px</span><span>8px</span></div></div><div class="grid-control"><label for="show-grid">Show layout grid</label><button id="show-grid" {...$gridRoot} use:gridRoot class="switch"><span></span></button></div><p class="hint">The grid is a visual aid. It does not change the poster’s text or spacing.</p><button class="primary" on:click={save} disabled={!!saved}>Save composition</button><button class="reset" on:click={reset}>Restore initial settings</button><p class="result" role="status">{saved||'This study changes only the local preview.'}</p></section><section class="poster" class:guides={$grid} style:text-align={$alignment} aria-label="Live typographic poster"><div class="poster-top"><span>TYPE / 2026</span><span>STUDY 04</span></div><div class="poster-orbit" aria-hidden="true"><i></i><b></b></div><div class="poster-title" style:letter-spacing={`${$tracking[0]}px`}><span>SPACE</span><span>TO</span><span>THINK.</span></div><div class="poster-bottom"><p>A pause between<br/>one idea and the next.</p><span>Original type study<br/>{$alignment} · {$tracking[0]}px</span></div></section></div></main>
Svelte 5.57.1에서 사용하는 @melt-ui/svelte 0.86.6의 검증된 스토어/액션 API. 명시적 빌더 구문을 사용하므로 선택 사항인 Melt 전처리기가 필요하지 않습니다. 별도의 차세대 runes API와는 다릅니다.
자바스크립트
// npm create vite@latest melt-type-study -- --template svelte
// cd melt-type-study && npm install
// npm install [email protected] @melt-ui/[email protected]
// Save the complete examples as src/App.svelte and src/style.css.
// src/main.js:
import {mount} from 'svelte';
import App from './App.svelte';
import './style.css';
mount(App, {target:document.querySelector('#app')});
// index.html: <div id="app"></div><script type="module" src="/src/main.js"></script>
// npm run dev
프로젝트를 위한 프롬프트
에이전트 프롬프트
타이포그래피 구성 연구에는 Svelte 5와 함께 @melt-ui/svelte 0.86.6의 스토어/액션 빌더를 사용하세요. createRadioGroup, createSlider, createSwitch 요소에 속성과 액션을 모두 연결하고, 별도로 명시되지 않은 전처리기를 요구하지 않는 제공된 명시적 구문을 사용하세요. 슬라이더의 단일 요소 배열과 elements.thumbs 계약, 정렬 값, 그리드 표시 여부를 그대로 유지하세요. 포스터의 계산된 스타일을 이 스토어에 직접 연결하고, 변경 후에는 저장된 피드백을 무효화한 다음 left, [2], true로 초기화하세요. 최신 runes API나 예약 모델을 함께 사용하지 마세요.
05
Melt UI 디자인 시스템 출처 및 범위
Svelte 5.57.1의 @melt-ui/svelte 0.86.6 스토어/액션 빌더. 모든 시각적 토큰과 포스터 콘텐츠는 오리지널입니다. 저장 기능은 로컬 미리보기 상태만 기록하며 디자인을 내보내거나 게시하지 않습니다.