๐ Motion ๋ผ์ด๋ธ ๋ฐ๋ชจ ๋ฐ๋ก๊ฐ๊ธฐ
๋ ํฌ๋ฅผ ํด๋ก ํ์ง ์๊ณ ๋ ๋ฐ๋ก ๋ธ๋ผ์ฐ์ ์์ ๋ชจ๋ ๋ชจ๋ฌ ํ์ ์ ํ ์คํธํด๋ณผ ์ ์์ต๋๋ค.
- iOS Safari: ์๋จ ๋งํฌ ํด๋ฆญ ํ ๊ฐ๋ฐ์๋๊ตฌ โ ๋๋ฐ์ด์ค ์๋ฎฌ๋ ์ด์
- Android Chrome: ์๋จ ๋งํฌ ํด๋ฆญ ํ F12 โ ๋ชจ๋ฐ์ผ ๋ทฐ ํ ๊ธ
- ์ค์ ๋ชจ๋ฐ์ผ: QR ์ฝ๋๋ก ์ ๊ทผ (QR ์์ฑ๊ธฐ์ ์ URL ์ ๋ ฅ)
Motion์ ๋ค์ํ ๋๋ฐ์ด์ค์ ํ๋ฉด ํฌ๊ธฐ์ ์ต์ ํ๋ 7๊ฐ์ง ๋ชจ๋ฌ ํ์ ์ ์ ๊ณตํ๋ ๋ฐ์ํ ๋ชจ๋ฌ ์ ๋๋ฉ์ด์ ์์คํ ์ ๋๋ค. ๋ชจ๋ฐ์ผ๊ณผ PC ํ๊ฒฝ์์ ๊ฐ๊ฐ ๋ค๋ฅธ UX ํจํด์ ์ ์ฉํ์ฌ ์ต์ ์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
- ๋ฐ์ํ ๋์์ธ: ๋ชจ๋ฐ์ผ/PC ํ๊ฒฝ๋ณ ์ต์ ํ๋ ๋ชจ๋ฌ ๋ ์ด์์
- ๋ถ๋๋ฌ์ด ์ ๋๋ฉ์ด์ : CSS3์ JavaScript๋ฅผ ๊ฒฐํฉํ ๊ณ ์ฑ๋ฅ ์ ๋๋ฉ์ด์
- ์ค๋งํธ ๋ ์ด์์: ์ฝํ ์ธ ์์ ๋ฐ๋ฅธ ๋์ ๋ ์ด์์ ์กฐ์
- ์ ๊ทผ์ฑ: ํค๋ณด๋ ๋ด๋น๊ฒ์ด์ ๋ฐ ESC ํค ์ง์
์ด ๋ ํฌ์๋ ์คํ ์ค์ธ CSS/WAAPI ์ ๋๋ฉ์ด์ ์ ๊ด์ฐฐํด์ ์ฒจ๋ถ ์ด๋ฏธ์ง ๊ฐ์ โ๋ชจ์ ์คํ ์ํธโ๋ฅผ ์น์์ ๋ ๋๋งํ๋ ๋๊ตฌ๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
- ํด๋:
motion-spec-sheet/ - ๋ผ์ด๋ธ๋ฌ๋ฆฌ:
motion-spec-sheet/motion-spec-sheet.js - ๋ฌธ์:
motion-spec-sheet/README.md
HTML์ ์๋ ์คํฌ๋ฆฝํธ 1์ค๋ง ์ถ๊ฐํ๋ฉด Specs ๋ฒํผ/์ค์ UI๊ฐ ์๋ ์์ฑ๋ฉ๋๋ค.
<script src="motion-spec-sheet/motion-spec-sheet.js"></script>Specs๋ฒํผ: ์ฐ์ธก ํ๋จ์ ์๋ ์์ฑ (๋จ์ถํค: Ctrl+Shift+M)์ค์ : ๊ทธ๋ฃน(์ด๋ฆ/์ ๋ ํฐ) ๋ฑ๋ก ํ ์ด๋ค ํ๋ก์ ํธ์์๋ ๋์ผํ ๋ฐฉ์์ผ๋ก ์บก์ฒ ๊ฐ๋ฅCapture: ์๋ ์บก์ฒ ๋ฒํผ- Auto capture: ํด๋ฆญ/ํค ์ ๋ ฅ ์ดํ ์คํ ์ค ์ ๋๋ฉ์ด์ ์ ๊ฐ์งํด ์๋ ์บก์ฒ(์ต์ )
- ํ์๋ผ์ธ ๋ทฐ: Auto/Custom ๋ฒ์ ์ ์ญ ์ ์ฉ + ๋ฒ์ ์ด๊ณผ ์ โ์์ ํ์ฅโ ์๋ด
์์ธํ ์ฌ์ฉ๋ฒ์ motion-spec-sheet/README.md๋ฅผ ์ฐธ๊ณ ํ์ธ์.
๋ชจ๋ฌ ์์คํ
โโโ ๋ชจ๋ฐ์ผ ๋ชจ๋ฌ (4์ข
)
โ โโโ mo-small-center โ ์์ ์ผํฐ ๋ชจ๋ฌ
โ โโโ mo-small-bottom โ ์์ ๋ฐํ
์ํธ
โ โโโ mo-medium-bottom โ ์ค๊ฐ ๋ฐํ
์ํธ
โ โโโ mo-large-full โ ์ ์ฒด ํ๋ฉด ๋ชจ๋ฌ
โโโ PC ๋ชจ๋ฌ (3์ข
)
โโโ pc-small-center โ ์์ ์ผํฐ ๋ชจ๋ฌ
โโโ pc-medium-center โ ์ค๊ฐ ์ผํฐ ๋ชจ๋ฌ
โโโ pc-large-center โ ํฐ ์ผํฐ ๋ชจ๋ฌ
์ธ์ : DOM ๋ก๋ ์๋ฃ ์ (DOMContentLoaded)
์ด๋ป๊ฒ:
// ๋ชจ๋ ๋ชจ๋ฌ ํ์
์ ์ฌ์ ์์ฑํ์ฌ ์ฑ๋ฅ ์ต์ ํ
Object.keys(modalConfigs).forEach(type => {
// ํ
ํ๋ฆฟ ๋ณต์ ๋ฐ ๊ฐ ํ์
๋ณ ์ปค์คํฐ๋ง์ด์ง
const container = document.createElement('div');
container.id = `modal-container-${type}`;
// ... DOM ๊ตฌ์กฐ ์์ฑ
});๊ฒฐ๊ณผ: 7๊ฐ์ง ๋ชจ๋ฌ์ด hidden ์ํ๋ก ๋ฉ๋ชจ๋ฆฌ์ ์ค๋น๋จ
์ธ์ : ์ฌ์ฉ์๊ฐ ๋ชจ๋ฌ ํธ๋ฆฌ๊ฑฐ ๋ฒํผ ํด๋ฆญ ์ด๋ป๊ฒ:
triggerButtons.forEach(button => {
button.addEventListener('click', () => openModal(button.dataset.modalType));
});๋ฌด์์: Body ์คํฌ๋กค ๋ฐฉ์ง ๋ฐ ๋ชจ๋ฌ ์ปจํ ์ด๋ ํ์ฑํ
const openModal = (requestedType) => {
// 1. ์คํฌ๋กค ๋ฐฉ์ง
document.body.classList.add('overflow-hidden');
// 2. ๋ชจ๋ฌ ํ์
modalContainer.classList.remove('hidden');
// 3. ๋ค์ ํ๋ ์์์ ์ ๋๋ฉ์ด์
์์
requestAnimationFrame(() => {
// ์ ๋๋ฉ์ด์
์ ์ฉ...
});
};์ธ์ : ๋ชจ๋ฐ์ผ ๋ชจ๋ฌ์์ ์ฝํ ์ธ ๊ฐ ํ๋ฉด์ ์ด๊ณผํ ๋ ์ด๋ป๊ฒ:
const checkAndAdjustModalType = (modalContainer) => {
const totalContentHeight = modalHeader.offsetHeight +
mainContentElement.scrollHeight +
modalFooter.offsetHeight;
const availableHeight = modalContainer.clientHeight;
if (totalContentHeight > availableHeight) {
// ์ ์ฒด ํ๋ฉด ๋ชจ๋ฌ๋ก ์๋ ๋ณ๊ฒฝ
modalContainer.dataset.layoutType = 'mo-large-full';
applyLayoutOnly(modalContainer, 'mo-large-full');
}
};CSS ์ ๋๋ฉ์ด์ ์กฐํฉ:
/* ์: ๋ชจ๋ฐ์ผ ์์ ์ผํฐ ๋ชจ๋ฌ */
.modal-mo-small-center-open {
animation: modal-mo-small-center-open 300ms ease-out,
modal-opacity-open 100ms linear;
}
@keyframes modal-mo-small-center-open {
from { transform: translateY(50%); }
}๋ค์ค ๋ซ๊ธฐ ๋ฐฉ๋ฒ ์ง์:
modalWrapper.addEventListener('click', (e) => {
// 1. ์ค๋ฒ๋ ์ด ํด๋ฆญ
if (e.target.classList.contains('modal-overlay')) {
closeModal(e.target.parentElement);
}
// 2. ๋ซ๊ธฐ/ํ์ธ ๋ฒํผ ํด๋ฆญ
const button = e.target.closest('.modal-close-btn, .modal-ok-btn');
if (button) closeModal(modalContainer);
});
// 3. ESC ํค ์
๋ ฅ
window.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeModal(visibleModal);
}
});๋ชจ๋ฌ ํฌ๊ธฐ๋ณ ์ฐจ๋ฑ ๋๋ ์ด:
const OVERLAY_DELAYS = {
'small': '50ms', // ์์ ๋ชจ๋ฌ: ๋น ๋ฅธ ๋ฐฐ๊ฒฝ ํ์ด๋
'medium': '150ms', // ์ค๊ฐ ๋ชจ๋ฌ: ์ ๋นํ ๋๋ ์ด
'large': '250ms' // ํฐ ๋ชจ๋ฌ: ์ปจํ
์ธ ํฌ์ง์
์ข
๋ฃ ์์ ๊ณผ ๋ง์ถฐ ์์ฐ์ค๋ฌ์ด ํจ๊ณผ
};์ ๋๋ฉ์ด์ ์๋ฃ ๋๊ธฐ:
const closeModal = (modalContainer) => {
// 1. ๋ซ๊ธฐ ์ ๋๋ฉ์ด์
์์
modalOverlay.classList.add('animated', 'modal-overlay-close-dynamic');
modalContent.classList.add('animated', `modal-${type}-close`);
// 2. ์ ๋๋ฉ์ด์
์ง์์๊ฐ ๋์ ๊ณ์ฐ
const contentDuration = parseDuration(contentComputedStyle.animation);
const overlayDuration = parseDuration(overlayComputedStyle.animation);
const timeoutDuration = Math.max(contentDuration, overlayDuration);
// 3. ์ ๋๋ฉ์ด์
์๋ฃ ํ ์ ๋ฆฌ
setTimeout(() => {
modalContainer.classList.add('hidden');
// ๋ ์ด์์ ์์ ๋ณต๊ตฌ...
}, timeoutDuration);
};:root {
/* ํ์ด๋ฐ ๊ฐ */
--motion-timing-100: 100ms;
--motion-timing-300: 300ms;
--motion-timing-500: 500ms;
/* ์ด์ง ํจ์ */
--motion-easing-easeOut: cubic-bezier(0.2, 0, 0, 1);
--motion-easing-easeIn: cubic-bezier(1, 0, 0.8, 1);
/* ๋ฐฐ๊ฒฝ์ */
--modal-overlay-default: rgba(0, 0, 0, 0.5);
}| ํ์ | ์ด๊ธฐ ์ ๋๋ฉ์ด์ | ๋ซ๊ธฐ ์ ๋๋ฉ์ด์ | ์ง์์๊ฐ |
|---|---|---|---|
mo-small-center |
translateY(50%) โ translateY(0) |
translateY(0) โ translateY(50%) |
300ms / 200ms |
mo-small-bottom |
translateY(100%) โ translateY(0) |
translateY(0) โ translateY(100%) |
300ms / 200ms |
mo-medium-bottom |
translateY(100%) โ translateY(0) |
translateY(0) โ translateY(100%) |
400ms / 300ms |
mo-large-full |
translateX(100%) โ translateX(0) |
translateX(0) โ translateX(100%) |
500ms / 400ms |
| ํ์ | ์ด๊ธฐ ์ ๋๋ฉ์ด์ | ๋ซ๊ธฐ ์ ๋๋ฉ์ด์ | ์ง์์๊ฐ |
|---|---|---|---|
pc-small-center |
translateY(50%) โ translateY(0) |
translateY(0) โ translateY(50%) |
300ms / 200ms |
pc-medium-center |
translateY(30%) โ translateY(0) |
translateY(0) โ translateY(30%) |
400ms / 300ms |
pc-large-center |
translateY(15%) โ translateY(0) |
translateY(0) โ translateY(15%) |
500ms / 400ms |
์ธ์ : ์ฒ์๋ถํฐ mo-large-full ํ์
์ผ๋ก ์ค์ ๋ ๋ชจ๋ฌ์์๋ง ํ์ฑํ
์ค์: ์ฝํ
์ธ ์ค๋ฒํ๋ก์ฐ๋ก ์ธํด ๋์ ์ผ๋ก ํ์ฌ์ด์ฆ๋ก ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ์๋ ํจ๋ด๋ ์ค ํจ๊ณผ๊ฐ ์ ์ฉ๋์ง ์์
์ด๋ป๊ฒ:
#main-content.parallax-effect {
transform: translateX(-20%);
transition: transform 600ms ease-out;
}์ ์ฉ ์กฐ๊ฑด:
// ์๋ณธ ํ์
(originalType)์ด mo-large-full์ธ ๊ฒฝ์ฐ์๋ง ํจ๋ด๋ ์ค ์ ์ฉ
const originalType = modalContainer.dataset.originalType;
if (originalType === 'mo-large-full') {
mainContent.classList.add('parallax-effect');
}ํด์ ์กฐ๊ฑด:
// ๋ชจ๋ฌ ๋ซ์ ๋ ์๋ณธ ํ์
์ด mo-large-full์ธ ๊ฒฝ์ฐ์๋ง ํจ๋ด๋ ์ค ํด์
if (modalContainer.dataset.originalType === 'mo-large-full') {
mainContent.classList.remove('parallax-effect');
}๋ชฉ์ : ๋ชจ๋ฌ ํฌ๊ธฐ๋ณ ์ ์ ํ ์ฝํ ์ธ ์ ์ ๊ณต
const getParagraphCount = (type) => {
const sizeMap = { 'small': 1, 'medium': 2, 'large': 10 };
const size = Object.keys(sizeMap).find(s => type.includes(s)) || 'small';
return sizeMap[size];
};
// Lorem Ipsum ๋ฌธ๋จ์ ํฌ๊ธฐ๋ณ๋ก ๋์ ์์ฑ
loremContent.innerHTML = Array(paragraphCount)
.fill(baseLoremText)
.map(text => `<p class="lorem-paragraph">${text}</p>`)
.join('');๋ชฉ์ : CSS ์ ๋๋ฉ์ด์ ์๋ฃ ์์ ์ ํํ ๊ฐ์ง
const parseDuration = (animationStr) => {
// CSS animation ์์ฑ์์ duration + delay ์ถ์ถ
const timeValues = animationStr.match(/\d+(\.\d+)?(ms|s)/g) || [];
const timesInMs = timeValues.map(time => {
const value = parseFloat(time);
return time.endsWith('s') && !time.endsWith('ms') ? value * 1000 : value;
});
// ์ฒซ ๋ ๊ฐ(duration, delay)์ ํฉ ๋ฐํ
return timesInMs.slice(0, 2).reduce((sum, time) => sum + time, 0);
};๋ชฉ์ : ํ๋ฉด ํฌ๊ธฐ ๋ณ๊ฒฝ ์ ๋ชจ๋ฌ ๋ ์ด์์ ์๋ ์กฐ์
const handleResize = () => {
const visibleModal = modalWrapper.querySelector('.modal-container:not(.hidden)');
if (visibleModal) {
checkAndAdjustModalType(visibleModal);
}
};
// ๋๋ฐ์ด์ฑ์ผ๋ก ์ฑ๋ฅ ์ต์ ํ
window.addEventListener('resize', () => {
clearTimeout(resizeDebounceTimer);
resizeDebounceTimer = setTimeout(handleResize, 150);
});graph TD
A[๋ฒํผ ํด๋ฆญ] --> B[openModal ํธ์ถ]
B --> C[body ์คํฌ๋กค ๋ฐฉ์ง]
C --> D[๋ชจ๋ฌ ํ์]
D --> E[์ฝํ
์ธ ์ค๋ฒํ๋ก์ฐ ์ฒดํฌ]
E --> F{์ฝํ
์ธ ์ด๊ณผ?}
F -->|Yes| G[ํ์ฌ์ด์ฆ๋ก ๋ณ๊ฒฝ]
F -->|No| H[์๋ ๋ ์ด์์ ์ ์ง]
G --> I{์๋ณธ ํ์
์ด<br/>mo-large-full?}
H --> I
I -->|Yes| J[ํจ๋ด๋ ์ค ํจ๊ณผ ์ ์ฉ]
I -->|No| K[ํจ๋ด๋ ์ค ํจ๊ณผ ์์]
J --> L[์ด๊ธฐ ์ ๋๋ฉ์ด์
์คํ]
K --> L
graph TD
A[๋ซ๊ธฐ ํธ๋ฆฌ๊ฑฐ] --> B{ํธ๋ฆฌ๊ฑฐ ํ์
}
B -->|์ค๋ฒ๋ ์ด ํด๋ฆญ| C[closeModal ํธ์ถ]
B -->|๋ฒํผ ํด๋ฆญ| C
B -->|ESC ํค| C
C --> D{์๋ณธ ํ์
์ด<br/>mo-large-full?}
D -->|Yes| E[ํจ๋ด๋ ์ค ํจ๊ณผ ์ ๊ฑฐ]
D -->|No| F[ํจ๋ด๋ ์ค ํจ๊ณผ ์์]
E --> G[๋ซ๊ธฐ ์ ๋๋ฉ์ด์
์์]
F --> G
G --> H[์ค๋ฒ๋ ์ด ๋๋ ์ด ์ ์ฉ]
H --> I[์ ๋๋ฉ์ด์
์๋ฃ ๋๊ธฐ]
I --> J[๋ชจ๋ฌ ์จ๊น]
J --> K[๋ ์ด์์ ์์๋ณต๊ตฌ]
K --> L[body ์คํฌ๋กค ๋ณต์]
-
์ฝํ ์ธ ์ค๋ฒํ๋ก์ฐ ๊ฐ์ง
- ํค๋ + ๋ณธ๋ฌธ + ํธํฐ ๋์ด๊ฐ ํ๋ฉด ๋์ด ์ด๊ณผ ์
- ์๋์ผ๋ก
mo-large-full๋ ์ด์์์ผ๋ก ์ ํ
-
๋ฐํ ์ํธ ํจํด
mo-small-bottom,mo-medium-bottom์์ ํ๋จ์์ ์ฌ๋ผ์ค๋ ์ ๋๋ฉ์ด์ - iOS/Android ๋ค์ดํฐ๋ธ UX์ ์ผ์น
-
์ ์ฒด ํ๋ฉด ๋ชจ๋ฌ
mo-large-full์์ ์ฐ์ธก์์ ์ฌ๋ผ์ด๋ ์ธ- ์ฒ์๋ถํฐ
mo-large-fullํ์ ์ธ ๊ฒฝ์ฐ์๋ง ๋ฐฐ๊ฒฝ ํจ๋ด๋ ์ค ํจ๊ณผ๋ก ๊น์ด๊ฐ ์ฐ์ถ - ๋์ ์ผ๋ก ํ์ฌ์ด์ฆ ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ๋ ํจ๋ด๋ ์ค ํจ๊ณผ ์์
-
์ผํฐ ์ ๋ ฌ
- ๋ชจ๋ PC ๋ชจ๋ฌ์ ํ๋ฉด ์ค์ ๋ฐฐ์น
- ํฌ๊ธฐ๋ณ ๋ค๋ฅธ Y์ถ ์ด๋ ๊ฑฐ๋ฆฌ๋ก ์์ฐ์ค๋ฌ์ด ๋ฑ์ฅ๊ฐ
-
ํฌ๊ธฐ๋ณ ์ฐจ๋ฑํ
- Small: 50% Y ์ด๋ (๋น ๋ฅธ ๋ฑ์ฅ)
- Medium: 30% Y ์ด๋ (์ ๋นํ ๋ฑ์ฅ)
- Large: 15% Y ์ด๋ (๋ถ๋๋ฌ์ด ๋ฑ์ฅ)
- GPU ๊ฐ์ ํ์ฉ
.modal-body, .modal-overlay {
will-change: transform, opacity;
transform: translateZ(0);
backface-visibility: hidden;
}-
์ฌ์ ๋ ๋๋ง
- ๋ชจ๋ ๋ชจ๋ฌ DOM์ ์ด๊ธฐํ ์์ ์ ์์ฑ
- ๋ฐํ์ DOM ์กฐ์ ์ต์ํ
-
๋๋ฐ์ด์ฑ
- ๋ฆฌ์ฌ์ด์ฆ ์ด๋ฒคํธ 150ms ๋๋ฐ์ด์ฑ
- ๋ถํ์ํ ๋ ์ด์์ ์ฌ๊ณ์ฐ ๋ฐฉ์ง
-
์ด๋ฒคํธ ์์
- ๋จ์ผ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ก ๋ชจ๋ ๋ชจ๋ฌ ์ ์ด
- ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง
-
์ ๋๋ฉ์ด์ ์ํ ์ ๋ฆฌ
// ์ ๋๋ฉ์ด์
์๋ฃ ํ GPU ๊ฐ์ ํด์
.modal-container.hidden .modal-body,
.modal-container.hidden .modal-overlay {
will-change: auto;
}์๋ก์ด ๋ชจ๋ฌ ํ์ ์ถ๊ฐ ์ ํ์ํ ๋จ๊ณ:
- modalConfigs ๊ฐ์ฒด์ ์ค์ ์ถ๊ฐ
const modalConfigs = {
// ๊ธฐ์กด ์ค์ ...
'new-modal-type': {
container: 'items-center justify-center p-8',
content: 'rounded-xl w-full max-w-sm'
}
};- CSS ํคํ๋ ์ ์ ๋๋ฉ์ด์ ์ ์
@keyframes modal-new-modal-type-open {
from { transform: scale(0.8); opacity: 0; }
}
@keyframes modal-new-modal-type-close {
to { transform: scale(0.8); opacity: 0; }
}- ์ ๋๋ฉ์ด์ ํด๋์ค ์คํ์ผ ์์ฑ
.modal-new-modal-type-open {
animation: modal-new-modal-type-open 400ms ease-out;
}
.modal-new-modal-type-close {
animation: modal-new-modal-type-close 300ms ease-in forwards;
}- ์ต์ ์๊ตฌ์ฌํญ: ES6+ ์ง์ ๋ธ๋ผ์ฐ์
- CSS ์ง์:
animation,transform,requestAnimationFrame - ๊ถ์ฅ ๋ธ๋ผ์ฐ์ : Chrome 60+, Firefox 55+, Safari 12+
-
๋๋ ์ฝํ ์ธ ์ฒ๋ฆฌ
- 10๊ฐ ์ด์์ ๋ฌธ๋จ์์ ์ฑ๋ฅ ์ ํ ๊ฐ๋ฅ
- ๊ฐ์ ์คํฌ๋กค๋ง ๊ตฌํ ๊ถ์ฅ
-
๋์ ๋ชจ๋ฌ ์ ํ
- ํ์ฌ ๊ตฌ์กฐ์ ๋จ์ผ ๋ชจ๋ฌ๋ง ์ง์
- ์ค์ฒฉ ๋ชจ๋ฌ ํ์ ์ ์ํคํ ์ฒ ์์ ํ์
- ARIA ๋ผ๋ฒจ๋ง ๋ฏธ์ ์ฉ
- ํฌ์ปค์ค ํธ๋ํ ๋ฏธ๊ตฌํ
- ์คํฌ๋ฆฐ ๋ฆฌ๋ ์ต์ ํ ํ์
<!-- ๋ชจ๋ฌ ํธ๋ฆฌ๊ฑฐ ๋ฒํผ -->
<button data-modal-type="pc-medium-center" class="modal-trigger">
์ค๊ฐ ํฌ๊ธฐ ๋ชจ๋ฌ ์ด๊ธฐ
</button>// ๋ชจ๋ฌ ์ด๊ธฐ
openModal('mo-small-center');
// ํน์ ๋ชจ๋ฌ ๋ซ๊ธฐ
const modalContainer = document.getElementById('modal-container-pc-large-center');
closeModal(modalContainer);
// ํ์ฌ ์ด๋ฆฐ ๋ชจ๋ฌ ํ์ธ
const visibleModal = document.querySelector('.modal-container:not(.hidden)');- ์ค์ฒฉ ๋ชจ๋ฌ ์ง์
- ์ปค์คํ ์ ๋๋ฉ์ด์ ์ค์ API
- ๋ชจ๋ฌ ํ์คํ ๋ฆฌ ๊ด๋ฆฌ
- ์ ๊ทผ์ฑ ๊ฐ์ (ARIA, Focus trap)
- TypeScript ์ง์
- ๋ชจ๋ฌ ๋๋๊ทธ ์ค ๋๋กญ
- ๋ชจ๋ฌ ์ํ ๊ด๋ฆฌ (Redux/Zustand)
- ์๋ฒ์ฌ์ด๋ ๋ ๋๋ง ์ง์
- ๋ชจ๋ฐ์ผ ์ ์ค์ฒ ์ธ์
์ด ๋ฌธ์๋ Motion ๋ชจ๋ฌ ์์คํ v1.0์ ๊ธฐ์ค์ผ๋ก ์์ฑ๋์์ต๋๋ค.