【Next.js】【モーダル】Micromodal.jsを使った例

2024-10-13 作成

この記事でわかること

  • 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: 実装予定

参考

実装メモ帳