์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- ํ๋ก ํธ์๋
- ์นUI
- ์ธํฐ๋ ํฐ๋ธui
- ์คํฌ๋กค์ ๋๋ฉ์ด์
- ui์ปดํฌ๋ํธ
- UIUX
- js์คํฌ๋กค์ด๋ฒคํธ
- ์ฝ๋ฉ์์
- css์ ๋๋ฉ์ด์
- ๋ฐ๋๋ผjs
- preferscolorscheme
- css๋ณ์
- ๋ฐ๋๋ผ์คํฌ๋ฆฝํธ
- ์น๊ฐ๋ฐ
- ๋ฐ์ํ์น
- JavaScript
- ์๋ฐ์คํฌ๋ฆฝํธ์ปดํฌ๋ํธ
- ์น๋์์ธ
- ์๋ฐ์คํฌ๋ฆฝํธui
- ์ ๊ทผ์ฑ
- uiux๋์์ธ
- ์น์ ๊ทผ์ฑ
- ๋คํฌ๋ชจ๋
- ์นui๋์์ธ
- UX๋์์ธ
- ํ ๋ง์๋์ ํ
- ๋ก์ปฌ์คํ ๋ฆฌ์ง
- ๋คํฌ๋ชจ๋ํ ๊ธ
- ์คํฌ๋ฆฐ๋ฆฌ๋์ง์
- ์๋ฐ์คํฌ๋ฆฝํธ
- Today
- Total
UI Code Lab
์ ๊ทผ์ฑ์ ๊ณ ๋ คํ ์ค์ ๋ชจ๋ฌ ๊ตฌํ๊ธฐ (Modal) ๋ณธ๋ฌธ
์ ๊ทผ์ฑ์ ๊ณ ๋ คํ ์ค์ ๋ชจ๋ฌ ๊ตฌํ๊ธฐ (Modal)
๐ฏ๊ฟ์์ด๐ 2025. 6. 26. 11:57
๋ชจ๋ฌ์ UI์์ ์์ฃผ ์ฐ์ด๋ ์ปดํฌ๋ํธ์ง๋ง,
์ ๊ทผ์ฑ๊น์ง ์ฑ๊ธด ๊ตฌํ์ ์๊ฐ๋ณด๋ค ๋๋ญ ๋๋ค.
์ด ๊ธ์์๋ ๊ธฐ๋ณธ์ ์ธ ๋ฐ๋๋ผ ์๋ฐ์คํฌ๋ฆฝํธ ๋ชจ๋ฌ ๊ตฌํ๋ฒ๋ถํฐ,
์คํฌ๋ฆฐ๋ฆฌ๋์ ํค๋ณด๋ ์ฌ์ฉ์๋ ๊ณ ๋ คํ ์ ๊ทผ์ฑ ๊ฐ์ ํฌ์ธํธ๊น์ง ๋ค๋ค๋ด ๋๋ค.
์ค๋ฌด์ ๋ฐ๋ก ์ ์ฉํ ์ ์๋ ์์ ์ฝ๋๋ ํจ๊ป ์ ๊ณตํ๋,
๋ณต์กํ ํ๋ ์์ํฌ ์์ด ์์ JS๋ก๋ ์ถฉ๋ถํ ๋ฉ์ง ์ ๊ทผ์ฑ ๋ชจ๋ฌ์ ๋ง๋ค์ด๋ณด์ธ์!
โจ ๊ธฐ๋ณธ ๋ชจ๋ฌ ๊ตฌํ
๊ฐ๋จํ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋ฒํผ ํด๋ฆญ ์ ๋ชจ๋ฌ์ ๋์ฐ๊ณ ๋ซ๋ ๊ธฐ๋ณธ ๊ตฌ์กฐ์ ๋๋ค.
โ HTML ๊ตฌ์กฐ
<button id="openModal">๋ชจ๋ฌ ์ด๊ธฐ</button>
<div id="modal" class="modal" hidden>
<div class="modal-content" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
<h2 id="modalTitle">๋ชจ๋ฌ ์ ๋ชฉ</h2>
<p>๋ชจ๋ฌ ๋ด์ฉ์
๋๋ค.</p>
<button id="closeModal">๋ซ๊ธฐ</button>
</div>
</div>
โ JS ๊ธฐ๋ณธ ๋ก์ง
const openBtn = document.getElementById('openModal');
const closeBtn = document.getElementById('closeModal');
const modal = document.getElementById('modal');
openBtn.addEventListener('click', () => {
modal.hidden = false;
});
closeBtn.addEventListener('click', () => {
modal.hidden = true;
});
โ ๊ธฐ๋ณธ ์คํ์ผ (์ ํ)
.modal {
position: fixed;
inset: 0;
background-color: rgba(0,0,0,0.4);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 2em;
border-radius: 8px;
}
โฟ ์ ๊ทผ์ฑ์ ๋ฐ์ํ ๋ชจ๋ฌ ๊ฐ์ ํฌ์ธํธ
์ ์ฝ๋์ ์ฝ๊ฐ์ ๊ธฐ๋ฅ๋ง ์ถ๊ฐํ๋ฉด ํจ์ฌ ์ ๊ทผ์ฑ ๋์ ๋ชจ๋ฌ์ด ๋ฉ๋๋ค.
์ฌ๊ธฐ์๋ ์ด์ ์๋น์ค์ ์ค์ ์ ์ฉํ๋ ์ ๊ทผ์ฑ ์ฌ๋ก๋ค์ ๊ธฐ๋ฐ์ผ๋ก ์ค๋ช ํฉ๋๋ค.
1. ํฌ์ปค์ค ํธ๋ฉ
๋ชจ๋ฌ ์์์ Tab ํค๋ก ํฌ์ปค์ค๊ฐ ์ํํ๋๋ก ์ค์ ํฉ๋๋ค.
๋ชจ๋ฌ์ด ์ด๋ฆฌ๋ฉด ํฌ์ปค์ค๊ฐ ์ฒซ ๋ฒ์งธ ํฌ์ปค์ค ๊ฐ๋ฅํ ์์๋ก ์ด๋ํ๊ณ ,
Shift + Tab์ด๋ Tab์ผ๋ก ํฌ์ปค์ค๊ฐ ๋ฐ๊นฅ์ผ๋ก ๋๊ฐ์ง ์๊ฒ ๋ง๋ ๋ฐฉ์์ ๋๋ค.
const focusableSelectors = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
const modalContent = modal.querySelector('.modal-content');
function trapFocus(e) {
const focusableElements = modalContent.querySelectorAll(focusableSelectors);
const firstEl = focusableElements[0];
const lastEl = focusableElements[focusableElements.length - 1];
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstEl) {
e.preventDefault();
lastEl.focus();
} else if (!e.shiftKey && document.activeElement === lastEl) {
e.preventDefault();
firstEl.focus();
}
}
}
2. ESC ํค๋ก ๋ซ๊ธฐ
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && !modal.hidden) {
modal.hidden = true;
openBtn.focus();
}
});
3. ARIA ์์ฑ ์ ์ฉ
- role="dialog": ์คํฌ๋ฆฐ๋ฆฌ๋๊ฐ ๋ํ์ฐฝ์์ ์ธ์งํฉ๋๋ค.
- aria-modal="true": ๋ฐฐ๊ฒฝ๊ณผ ๋ถ๋ฆฌ๋ ๋ ๋ฆฝ ๋ ์ด์ด์์ ์๋ฆฝ๋๋ค.
- aria-labelledby="modalTitle": ํ์ดํ์ ์ฐธ์กฐํด ์ ๋ชฉ์ ์๋ ค์ค๋๋ค.
4. ํฌ์ปค์ค ๋ณต๊ท
๋ชจ๋ฌ์ ๋ซ์ ๋๋ ๋ค์ ํธ๋ฆฌ๊ฑฐ ๋ ์์(์: "๋ชจ๋ฌ ์ด๊ธฐ" ๋ฒํผ)๋ก ํฌ์ปค์ค๋ฅผ ๋๋๋ ค ์ฌ์ฉ์ ํ๋ฆ์ ๋์ง ์์์ผ ํฉ๋๋ค.
โ ๊ตฌ์กฐ์ ·์๊ฐ์ ๊ฐ์ ์์ด๋์ด
๐ฏ 1. ์ ๋๋ฉ์ด์ ์ผ๋ก ๋ถ๋๋ฌ์ด ์ ํ ํจ๊ณผ ์ถ๊ฐ
๋ชจ๋ฌ์ด ์ด๋ฆฌ๊ณ ๋ซํ ๋ ๋ถ๋๋ฌ์ด ํธ๋์ง์ ์ ์ฃผ๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ํจ์ฌ ์์ฐ์ค๋ฌ์์ง๋๋ค.
.modal[hidden] {
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.modal {
opacity: 1;
transition: opacity 0.3s ease;
}
JS์์๋ modal.hidden = false; ๋์ modal.classList.add('visible'),
๋ซ์ ๋ remove('visible')๋ก ํ ๊ธ ํ๋ฉด์ ์ ๋๋ฉ์ด์ ์ ์ ์ฉํ๋ฉด ์ข์์.
๐ง 2. ๋ชจ๋ฌ ์ฌ๋ฌ ๊ฐ ๋์์ ์ํ ๊ตฌ์กฐ ์ผ๋ฐํ
๋ชจ๋ฌ์ด ํ๋๋ฟ์ด ์๋ ๊ฒฝ์ฐ๋ฅผ ๋๋นํด,
JS ๊ตฌ์กฐ๋ฅผ ํจ์ํ์ผ๋ก ๋ง๋ค์ด๋๋ฉด ์ฌ์ฌ์ฉ์ฑ์ด ์ข์์ง๋๋ค.
function openModal(modalId) {
const modal = document.getElementById(modalId);
modal.hidden = false;
trapFocus(modal); // ํฌ์ปค์ค ํธ๋ฉ ํจ์ ์ฌํ์ฉ
}
function closeModal(modalId) {
const modal = document.getElementById(modalId);
modal.hidden = true;
}
์ถํ CAD ๋ค์ด๋ก๋๋ ๊ณต์ง์ฌํญ ํ์ ๋ฑ์๋ ๋์ผํ ๊ตฌ์กฐ๋ก ๋์ํ ์ ์์ด์.
๐งญ 3. ๋ฐฐ๊ฒฝ ํด๋ฆญ์ผ๋ก ๋ชจ๋ฌ ๋ซ๊ธฐ ๊ธฐ๋ฅ
์์ธ๋ก ์ฌ์ฉ์๊ฐ ์์ฐ์ค๋ฝ๊ฒ ๊ธฐ๋ํ๋ ๊ธฐ๋ฅ ์ค ํ๋์์.
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.hidden = true;
openBtn.focus();
}
});
๐ชก 4. CSS ๋ณ์ ํ์ฉํ ํ ๋ง ์คํ์ผ
๋ชจ๋ฌ ๋ฐฐ๊ฒฝ์, ๊ทธ๋ฆผ์, ์ฌ๋ฐฑ ๋ฑ์ CSS ๋ณ์๋ก ์ถ์ถํ๋ฉด
์ฌ๋ฌ ํ๊ฒฝ์ ์ฌ์ฌ์ฉํ๊ธฐ ์ฌ์์.
:root {
--modal-bg: #fff;
--modal-overlay: rgba(0, 0, 0, 0.4);
}
.modal {
background-color: var(--modal-overlay);
}
.modal-content {
background-color: var(--modal-bg);
}
๐ 5. ๋ค๊ตญ์ด ๋์์ ์ํ ํ ์คํธ ๊ด๋ฆฌ ๊ตฌ์กฐ
ํ๋ก์ ํธ์์ ๋ค๊ตญ์ด ํ์ด์ง๋ ์์๋ ๊ฑธ๋ก ๋ณด์ด๋๋ฐ์,
๋ชจ๋ฌ ํ ์คํธ๋ ์์ฑ ๊ธฐ๋ฐ์ผ๋ก ๋ถ๋ฆฌํด๋๋ฉด ๊ตญ์ ํ์ ์ ๋ฆฌํด์.
<p data-text="modal.message">๋ชจ๋ฌ ๋ด์ฉ์
๋๋ค.</p>
const i18n = {
ko: {
'modal.message': '๋ชจ๋ฌ ๋ด์ฉ์
๋๋ค.',
},
en: {
'modal.message': 'This is modal content.',
},
};
function applyLang(lang) {
document.querySelectorAll('[data-text]').forEach((el) => {
const key = el.getAttribute('data-text');
el.textContent = i18n[lang][key] || '';
});
}
๐ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์ ๊ทผ์ฑ์ ํ ๋ฒ์ ์๋ฒฝํ๊ฒ ํ๊ธฐ๋ณด๋ค,
๊พธ์คํ ๊ฐ์ ํด๊ฐ๋ ์ฌ์ ์ ๊ฐ๊น์์.
์ด ๊ธ์ด ์ฌ๋ฌ๋ถ์ด ๋ง๋ ๋ชจ๋ฌ ํ๋ํ๋์
๋ ๋ง์ ์ฌ์ฉ์๊ฐ ์ ๊ทผํ ์ ์๊ฒ ๋ง๋๋ ์์ ์ถ๋ฐ์ ์ด ๋์์ผ๋ฉด ํฉ๋๋ค.