この記事でわかること
- Micromodal.js を使ったモーダル実装の特徴と実装例
特徴
- キーボード操作、webアクセシビリティ要件を簡単に満たすことができる
- ios safari 15.3未満でも利用可能
- 以下の機能を簡単に実装できる
- モーダルが開かれた時に背景を固定(背面コンテンツのスクロールを抑止)する
- 背景要素クリックで、モーダルが閉じる
- 開閉アニメーション
実装例
src/components/Modal/index.tsx
# ...
const wrap = {
display: none;
&.is-open {
display: block;
}
}
const overlay = {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgb(0 0 0 / 70%);
}
type Props = {
children: ReactNode;
id: string;
};
export const Modal = ({ children, id }: Props) => {
const [isClient, setIsClient] = useState<boolean>(false);
useEffect(() => {
if (typeof window === 'undefined') return;
setIsClient(true);
}, []);
if (!isClient) return <></>; // SSR時にdocumentを参照しないようにしてます
return createPortal( // createPortal(任意のDOM, document.body) でbodyタグ直下の最後の要素として任意のDOMがレンダリングされます
<div aria-hidden="true" style={wrap} id={id}>
<div style={overlay} data-micromodal-close tabIndex={-1}>
<div aria-modal="true" role="dialog">
{children}
</div>
</div>
</div>,
document.body
);
};
src/components/Modal/hooks.tsx
# ...
import client from 'micromodal';
// モーダルの表示非表示ロジックをhooksにすることで、任意のコンポーネントで表示非表示ロジックを利用できるようにしてます。
export const useModal = (id: string) => {
const open = useCallback(() => {
client.show(id, {
disableScroll: true, // trueにすることで、モーダル表示時、bodyタグにoverflow: hidden;が付与されます。
});
}, [id]);
const close = useCallback(() => {
client.close(id);
}, [id]);
return { open, close };
};
src/pages/index.tsx
import { Modal } from '@/components/Modal';
import { useModal } from '@/components/Modal/hooks';
export const HOME = () => {
const { open, close } = useModal('modal');
return (
<>
<button onClick={open}>モーダルを開く</button>
<Modal id="modal">
<div css={styles.modal}>
<p>モーダル</p>
<button onClick={close}>モーダルを閉じる</button>
</div>
</Modal>
<>
);
};
完成!
追加実装 モーダルの開閉状態を URL で管理する
TODO: 実装予定